1
1
"""
2
- - A linked list is similar to an array, it holds values. However, links in a linked
3
- list do not have indexes.
4
- - This is an example of a double ended, doubly linked list.
5
- - Each link references the next link and the previous one.
6
- - A Doubly Linked List (DLL) contains an extra pointer, typically called previous
7
- pointer, together with next pointer and data which are there in singly linked list.
8
- - Advantages over SLL - It can be traversed in both forward and backward direction.
9
- Delete operation is more efficient"""
10
-
2
+ https://en.wikipedia.org/wiki/Doubly_linked_list
3
+ """
11
4
12
- class LinkedList :
13
- """
14
- >>> linked_list = LinkedList()
15
- >>> linked_list.insert_at_head("a")
16
- >>> linked_list.insert_at_tail("b")
17
- >>> linked_list.delete_tail()
18
- 'b'
19
- >>> linked_list.is_empty
20
- False
21
- >>> linked_list.delete_head()
22
- 'a'
23
- >>> linked_list.is_empty
24
- True
25
- """
26
5
27
- def __init__ (self ):
28
- self .head = None # First node in list
29
- self .tail = None # Last node in list
6
+ class Node :
7
+ def __init__ (self , data ):
8
+ self .data = data
9
+ self .previous = None
10
+ self .next = None
30
11
31
12
def __str__ (self ):
32
- current = self .head
33
- nodes = []
34
- while current is not None :
35
- nodes .append (current )
36
- current = current .next
37
- return " " .join (str (node ) for node in nodes )
38
-
39
- def insert_at_head (self , data ):
40
- new_node = Node (data )
41
- if self .is_empty :
42
- self .tail = new_node
43
- self .head = new_node
44
- else :
45
- self .head .previous = new_node
46
- new_node .next = self .head
47
- self .head = new_node
13
+ return f"{ self .data } "
48
14
49
- def delete_head (self ) -> str :
50
- if self .is_empty :
51
- return "List is empty"
52
15
53
- head_data = self .head .data
54
- if self .head .next :
55
- self .head = self .head .next
56
- self .head .previous = None
16
+ class DoublyLinkedList :
17
+ def __init__ (self ):
18
+ self .head = None
19
+ self .tail = None
20
+
21
+ def __iter__ (self ):
22
+ """
23
+ >>> linked_list = DoublyLinkedList()
24
+ >>> linked_list.insert_at_head('b')
25
+ >>> linked_list.insert_at_head('a')
26
+ >>> linked_list.insert_at_tail('c')
27
+ >>> tuple(linked_list)
28
+ ('a', 'b', 'c')
29
+ """
30
+ node = self .head
31
+ while node :
32
+ yield node .data
33
+ node = node .next
57
34
58
- else : # If there is no next previous node
59
- self .head = None
60
- self .tail = None
35
+ def __str__ (self ):
36
+ """
37
+ >>> linked_list = DoublyLinkedList()
38
+ >>> linked_list.insert_at_tail('a')
39
+ >>> linked_list.insert_at_tail('b')
40
+ >>> linked_list.insert_at_tail('c')
41
+ >>> str(linked_list)
42
+ 'a->b->c'
43
+ """
44
+ return "->" .join ([str (item ) for item in self ])
45
+
46
+ def __len__ (self ):
47
+ """
48
+ >>> linked_list = DoublyLinkedList()
49
+ >>> for i in range(0, 5):
50
+ ... linked_list.insert_at_nth(i, i + 1)
51
+ >>> len(linked_list) == 5
52
+ True
53
+ """
54
+ return len (tuple (iter (self )))
61
55
62
- return head_data
56
+ def insert_at_head (self , data ):
57
+ self .insert_at_nth (0 , data )
63
58
64
59
def insert_at_tail (self , data ):
60
+ self .insert_at_nth (len (self ), data )
61
+
62
+ def insert_at_nth (self , index : int , data ):
63
+ """
64
+ >>> linked_list = DoublyLinkedList()
65
+ >>> linked_list.insert_at_nth(-1, 666)
66
+ Traceback (most recent call last):
67
+ ....
68
+ IndexError: list index out of range
69
+ >>> linked_list.insert_at_nth(1, 666)
70
+ Traceback (most recent call last):
71
+ ....
72
+ IndexError: list index out of range
73
+ >>> linked_list.insert_at_nth(0, 2)
74
+ >>> linked_list.insert_at_nth(0, 1)
75
+ >>> linked_list.insert_at_nth(2, 4)
76
+ >>> linked_list.insert_at_nth(2, 3)
77
+ >>> str(linked_list)
78
+ '1->2->3->4'
79
+ >>> linked_list.insert_at_nth(5, 5)
80
+ Traceback (most recent call last):
81
+ ....
82
+ IndexError: list index out of range
83
+ """
84
+ if not 0 <= index <= len (self ):
85
+ raise IndexError ("list index out of range" )
65
86
new_node = Node (data )
66
- if self .is_empty :
67
- self .tail = new_node
87
+ if self .head is None :
88
+ self .head = self .tail = new_node
89
+ elif index == 0 :
90
+ self .head .previous = new_node
91
+ new_node .next = self .head
68
92
self .head = new_node
69
- else :
93
+ elif index == len ( self ) :
70
94
self .tail .next = new_node
71
95
new_node .previous = self .tail
72
96
self .tail = new_node
73
-
74
- def delete_tail (self ) -> str :
75
- if self .is_empty :
76
- return "List is empty"
77
-
78
- tail_data = self .tail .data
79
- if self .tail .previous :
97
+ else :
98
+ temp = self .head
99
+ for i in range (0 , index ):
100
+ temp = temp .next
101
+ temp .previous .next = new_node
102
+ new_node .previous = temp .previous
103
+ new_node .next = temp
104
+ temp .previous = new_node
105
+
106
+ def delete_head (self ):
107
+ return self .delete_at_nth (0 )
108
+
109
+ def delete_tail (self ):
110
+ return self .delete_at_nth (len (self ) - 1 )
111
+
112
+ def delete_at_nth (self , index : int ):
113
+ """
114
+ >>> linked_list = DoublyLinkedList()
115
+ >>> linked_list.delete_at_nth(0)
116
+ Traceback (most recent call last):
117
+ ....
118
+ IndexError: list index out of range
119
+ >>> for i in range(0, 5):
120
+ ... linked_list.insert_at_nth(i, i + 1)
121
+ >>> linked_list.delete_at_nth(0) == 1
122
+ True
123
+ >>> linked_list.delete_at_nth(3) == 5
124
+ True
125
+ >>> linked_list.delete_at_nth(1) == 3
126
+ True
127
+ >>> str(linked_list)
128
+ '2->4'
129
+ >>> linked_list.delete_at_nth(2)
130
+ Traceback (most recent call last):
131
+ ....
132
+ IndexError: list index out of range
133
+ """
134
+ if not 0 <= index <= len (self ) - 1 :
135
+ raise IndexError ("list index out of range" )
136
+ delete_node = self .head # default first node
137
+ if len (self ) == 1 :
138
+ self .head = self .tail = None
139
+ elif index == 0 :
140
+ self .head = self .head .next
141
+ self .head .previous = None
142
+ elif index == len (self ) - 1 :
143
+ delete_node = self .tail
80
144
self .tail = self .tail .previous
81
145
self .tail .next = None
82
- else : # if there is no previous node
83
- self .head = None
84
- self .tail = None
85
-
86
- return tail_data
146
+ else :
147
+ temp = self .head
148
+ for i in range (0 , index ):
149
+ temp = temp .next
150
+ delete_node = temp
151
+ temp .next .previous = temp .previous
152
+ temp .previous .next = temp .next
153
+ return delete_node .data
87
154
88
155
def delete (self , data ) -> str :
89
156
current = self .head
@@ -105,16 +172,55 @@ def delete(self, data) -> str:
105
172
current .next .previous = current .previous # 1 <--> 3
106
173
return data
107
174
108
- @property
109
- def is_empty (self ): # return True if the list is empty
110
- return self .head is None
175
+ def is_empty (self ):
176
+ """
177
+ >>> linked_list = DoublyLinkedList()
178
+ >>> linked_list.is_empty()
179
+ True
180
+ >>> linked_list.insert_at_tail(1)
181
+ >>> linked_list.is_empty()
182
+ False
183
+ """
184
+ return len (self ) == 0
111
185
112
186
113
- class Node :
114
- def __init__ (self , data ):
115
- self .data = data
116
- self .previous = None
117
- self .next = None
118
-
119
- def __str__ (self ):
120
- return f"{ self .data } "
187
+ def test_doubly_linked_list () -> None :
188
+ """
189
+ >>> test_doubly_linked_list()
190
+ """
191
+ linked_list = DoublyLinkedList ()
192
+ assert linked_list .is_empty () is True
193
+ assert str (linked_list ) == ""
194
+
195
+ try :
196
+ linked_list .delete_head ()
197
+ assert False # This should not happen.
198
+ except IndexError :
199
+ assert True # This should happen.
200
+
201
+ try :
202
+ linked_list .delete_tail ()
203
+ assert False # This should not happen.
204
+ except IndexError :
205
+ assert True # This should happen.
206
+
207
+ for i in range (10 ):
208
+ assert len (linked_list ) == i
209
+ linked_list .insert_at_nth (i , i + 1 )
210
+ assert str (linked_list ) == "->" .join (str (i ) for i in range (1 , 11 ))
211
+
212
+ linked_list .insert_at_head (0 )
213
+ linked_list .insert_at_tail (11 )
214
+ assert str (linked_list ) == "->" .join (str (i ) for i in range (0 , 12 ))
215
+
216
+ assert linked_list .delete_head () == 0
217
+ assert linked_list .delete_at_nth (9 ) == 10
218
+ assert linked_list .delete_tail () == 11
219
+ assert len (linked_list ) == 9
220
+ assert str (linked_list ) == "->" .join (str (i ) for i in range (1 , 10 ))
221
+
222
+
223
+ if __name__ == "__main__" :
224
+ from doctest import testmod
225
+
226
+ testmod ()
0 commit comments