Skip to content

Commit 9cb2c4f

Browse files
realDuYuanChaogithub-actions
authored andcommitted
Update doubly linked list (TheAlgorithms#3619)
* update doubly linked list * reformat code add more test * add test to iter * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
1 parent b6fecf2 commit 9cb2c4f

File tree

1 file changed

+185
-79
lines changed

1 file changed

+185
-79
lines changed

Diff for: data_structures/linked_list/doubly_linked_list.py

+185-79
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,156 @@
11
"""
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+
"""
114

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-
"""
265

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
3011

3112
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}"
4814

49-
def delete_head(self) -> str:
50-
if self.is_empty:
51-
return "List is empty"
5215

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
5734

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)))
6155

62-
return head_data
56+
def insert_at_head(self, data):
57+
self.insert_at_nth(0, data)
6358

6459
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")
6586
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
6892
self.head = new_node
69-
else:
93+
elif index == len(self):
7094
self.tail.next = new_node
7195
new_node.previous = self.tail
7296
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
80144
self.tail = self.tail.previous
81145
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
87154

88155
def delete(self, data) -> str:
89156
current = self.head
@@ -105,16 +172,55 @@ def delete(self, data) -> str:
105172
current.next.previous = current.previous # 1 <--> 3
106173
return data
107174

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
111185

112186

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

Comments
 (0)