Skip to content

Commit bbe2696

Browse files
authored
Added DoublyLinkedList (#55)
Co-authored-by: rohansingh9001
1 parent 4a67e3f commit bbe2696

File tree

7 files changed

+357
-3
lines changed

7 files changed

+357
-3
lines changed

AUTHORS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
Gagandeep Singh<[email protected]>
22
Kartikei Mittal<[email protected]>
33
4-
4+
Rohan Singh<[email protected]>

pydatastructs/linear_data_structures/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
from . import (
44
arrays,
5+
linked_lists
56
)
67

78
from .arrays import (
89
OneDimensionalArray,
910
DynamicOneDimensionalArray
1011
)
1112
__all__.extend(arrays.__all__)
13+
14+
from .linked_lists import (
15+
DoublyLinkedList
16+
)
17+
__all__.extend(linked_lists.__all__)
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
from __future__ import print_function, division
2+
from pydatastructs.utils.misc_util import _check_type, LinkedListNode
3+
4+
__all__ = [
5+
'DoublyLinkedList'
6+
]
7+
8+
class LinkedList(object):
9+
"""
10+
Abstract class for Linked List.
11+
"""
12+
__slots__ = ['head', 'size']
13+
14+
def __len__(self):
15+
return self.size
16+
17+
@property
18+
def is_empty(self):
19+
return self.size == 0
20+
21+
def __str__(self):
22+
"""
23+
For printing the linked list.
24+
"""
25+
elements = []
26+
current_node = self.head
27+
while current_node is not None:
28+
elements.append(current_node.data)
29+
current_node = current_node.next
30+
return str(elements)
31+
32+
class DoublyLinkedList(LinkedList):
33+
"""
34+
Represents Doubly Linked List
35+
36+
Examples
37+
========
38+
39+
>>> from pydatastructs import DoublyLinkedList
40+
>>> dll = DoublyLinkedList()
41+
>>> dll.append(6)
42+
>>> dll[0].data
43+
6
44+
>>> dll.head.data
45+
6
46+
>>> dll.append(5)
47+
>>> dll.append_left(2)
48+
>>> print(dll)
49+
[2, 6, 5]
50+
>>> dll[0].data = 7.2
51+
>>> dll.extract(1).data
52+
6
53+
>>> print(dll)
54+
[7.2, 5]
55+
56+
References
57+
==========
58+
59+
.. [1] https://en.wikipedia.org/wiki/Doubly_linked_list
60+
61+
"""
62+
__slots__ = ['head', 'tail', 'size']
63+
64+
def __new__(cls):
65+
obj = object.__new__(cls)
66+
obj.head = None
67+
obj.tail = None
68+
obj.size = 0
69+
return obj
70+
71+
def append_left(self, data):
72+
"""
73+
Pushes a new node at the start i.e.,
74+
the left of the list.
75+
76+
Parameters
77+
==========
78+
79+
data
80+
Any valid data to be stored in the node.
81+
"""
82+
self.insert_at(0, data)
83+
84+
def append(self, data):
85+
"""
86+
Appends a new node at the end of the list.
87+
88+
Parameters
89+
==========
90+
91+
data
92+
Any valid data to be stored in the node.
93+
"""
94+
self.insert_at(self.size, data)
95+
96+
def insert_after(self, prev_node, data):
97+
"""
98+
Inserts a new node after the prev_node.
99+
100+
Parameters
101+
==========
102+
103+
prev_node: LinkedListNode
104+
The node after which the
105+
new node is to be inserted.
106+
107+
data
108+
Any valid data to be stored in the node.
109+
"""
110+
self.size += 1
111+
new_node = LinkedListNode(data,
112+
links=['next', 'prev'],
113+
addrs=[None, None])
114+
new_node.next = prev_node.next
115+
prev_node.next = new_node
116+
new_node.prev = prev_node
117+
118+
if new_node.next is None:
119+
self.tail = new_node
120+
121+
def insert_before(self, next_node, data):
122+
"""
123+
Inserts a new node before the new_node.
124+
125+
Parameters
126+
==========
127+
128+
next_node: LinkedListNode
129+
The node before which the
130+
new node is to be inserted.
131+
132+
data
133+
Any valid data to be stored in the node.
134+
"""
135+
self.size += 1
136+
new_node = LinkedListNode(data,
137+
links=['next', 'prev'],
138+
addrs=[None, None])
139+
new_node.prev = next_node.prev
140+
next_node.prev = new_node
141+
new_node.next = next_node
142+
143+
if new_node.prev is None:
144+
self.head = new_node
145+
146+
def insert_at(self, index, data):
147+
"""
148+
Inserts a new node at the input index.
149+
150+
Parameters
151+
==========
152+
153+
index: int
154+
An integer satisfying python indexing properties.
155+
156+
data
157+
Any valid data to be stored in the node.
158+
"""
159+
if self.size == 0 and (index in (0, -1)):
160+
index = 0
161+
162+
if index < 0:
163+
index = self.size + index
164+
165+
if index > self.size:
166+
raise IndexError('%d index is out of range.'%(index))
167+
168+
self.size += 1
169+
new_node = LinkedListNode(data,
170+
links=['next', 'prev'],
171+
addrs=[None, None])
172+
if self.size == 1:
173+
self.head, self.tail = \
174+
new_node, new_node
175+
else:
176+
counter = 0
177+
current_node = self.head
178+
prev_node = None
179+
while counter != index:
180+
prev_node = current_node
181+
current_node = current_node.next
182+
counter += 1
183+
new_node.prev = prev_node
184+
new_node.next = current_node
185+
if prev_node is not None:
186+
prev_node.next = new_node
187+
if current_node is not None:
188+
current_node.prev = new_node
189+
if new_node.next is None:
190+
self.tail = new_node
191+
if new_node.prev is None:
192+
self.head = new_node
193+
194+
def pop_left(self):
195+
"""
196+
Extracts the Node from the left
197+
i.e. start of the list.
198+
199+
Returns
200+
=======
201+
202+
old_head: LinkedListNode
203+
The leftmost element of linked
204+
list.
205+
"""
206+
self.extract(0)
207+
208+
def pop_right(self):
209+
"""
210+
Extracts the node from the right
211+
of the linked list.
212+
213+
Returns
214+
=======
215+
216+
old_tail: LinkedListNode
217+
The leftmost element of linked
218+
list.
219+
"""
220+
self.extract(-1)
221+
222+
def extract(self, index):
223+
"""
224+
Extracts the node at the index of the list.
225+
226+
Parameters
227+
==========
228+
229+
index: int
230+
An integer satisfying python indexing properties.
231+
232+
Returns
233+
=======
234+
235+
current_node: LinkedListNode
236+
The node at index i.
237+
"""
238+
if self.is_empty:
239+
raise ValueError("The list is empty.")
240+
241+
if index < 0:
242+
index = self.size + index
243+
244+
if index >= self.size:
245+
raise IndexError('%d is out of range.'%(index))
246+
247+
self.size -= 1
248+
counter = 0
249+
current_node = self.head
250+
prev_node = None
251+
while counter != index:
252+
prev_node = current_node
253+
current_node = current_node.next
254+
counter += 1
255+
if prev_node is not None:
256+
prev_node.next = current_node.next
257+
if current_node.next is not None:
258+
current_node.next.prev = prev_node
259+
if index == 0:
260+
self.head = current_node.next
261+
if index == self.size:
262+
self.tail = current_node.prev
263+
return current_node
264+
265+
def __getitem__(self, index):
266+
"""
267+
Returns
268+
=======
269+
270+
current_node: LinkedListNode
271+
The node at given index.
272+
"""
273+
if index < 0:
274+
index = self.size + index
275+
276+
if index >= self.size:
277+
raise IndexError('%d index is out of range.'%(index))
278+
279+
counter = 0
280+
current_node = self.head
281+
while counter != index:
282+
current_node = current_node.next
283+
counter += 1
284+
return current_node
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from pydatastructs.linear_data_structures import DoublyLinkedList
2+
from pydatastructs.utils.raises_util import raises
3+
import copy, random
4+
5+
def test_DoublyLinkedList():
6+
random.seed(1000)
7+
dll = DoublyLinkedList()
8+
assert raises(IndexError, lambda: dll[2])
9+
dll.append_left(5)
10+
dll.append(1)
11+
dll.append_left(2)
12+
dll.append(3)
13+
dll.insert_after(dll[1], 4)
14+
dll.insert_after(dll[-1], 6)
15+
dll.insert_before(dll[0], 1)
16+
dll.insert_at(0, 2)
17+
dll.insert_at(-1, 9)
18+
dll.extract(2)
19+
dll.extract(0)
20+
dll.extract(-1)
21+
dll[-2].data = 0
22+
assert str(dll) == "[1, 5, 4, 1, 0, 9]"
23+
assert len(dll) == 6
24+
assert raises(IndexError, lambda: dll.insert_at(7, None))
25+
assert raises(IndexError, lambda: dll.extract(20))
26+
dll_copy = copy.deepcopy(dll)
27+
for i in range(len(dll)):
28+
if i%2 == 0:
29+
dll.pop_left()
30+
else:
31+
dll.pop_right()
32+
assert str(dll) == "[]"
33+
for _ in range(len(dll_copy)):
34+
index = random.randint(0, len(dll_copy) - 1)
35+
dll_copy.extract(index)
36+
assert str(dll_copy) == "[]"
37+
assert raises(ValueError, lambda: dll_copy.extract(1))

pydatastructs/utils/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from . import misc_util
44
from .misc_util import (
5-
TreeNode
5+
TreeNode,
6+
LinkedListNode
67
)
78
__all__.extend(misc_util.__all__)

pydatastructs/utils/misc_util.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from __future__ import print_function, division
22

33
__all__ = [
4-
'TreeNode'
4+
'TreeNode',
5+
'LinkedListNode'
56
]
67

78
_check_type = lambda a, t: isinstance(a, t)
@@ -40,3 +41,27 @@ def __str__(self):
4041
Used for printing.
4142
"""
4243
return str((self.left, self.key, self.data, self.right))
44+
45+
class LinkedListNode(object):
46+
"""
47+
Represents node in linked lists.
48+
49+
Parameters
50+
==========
51+
52+
data
53+
Any valid data to be stored in the node.
54+
"""
55+
56+
# __slots__ = ['data']
57+
58+
def __new__(cls, data=None, links=['next'], addrs=[None]):
59+
obj = object.__new__(cls)
60+
obj.data = data
61+
for link, addr in zip(links, addrs):
62+
obj.__setattr__(link, addr)
63+
obj.__slots__ = ['data'] + links
64+
return obj
65+
66+
def __str__(self):
67+
return str(self.data)

pydatastructs/utils/raises_util.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ def raises(exception, code):
1414
"""
1515
with pytest.raises(exception):
1616
code()
17+
return True

0 commit comments

Comments
 (0)