Skip to content

Commit f4f4c16

Browse files
committed
Added utility for doubly-linked lists
1 parent aa6f8ad commit f4f4c16

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

2020/doubly_linked_list.py

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
class DoublyLinkedList:
2+
def __init__(self, is_cycle=False):
3+
"""
4+
Creates a list
5+
6+
:param Boolean is_cycle: Whether the list is a cycle (loops around itself)
7+
"""
8+
self.start_element = None
9+
self.is_cycle = is_cycle
10+
self.elements = {}
11+
12+
def insert(self, ref_element, new_elements, insert_before=False):
13+
"""
14+
Inserts new elements in the list
15+
16+
:param Any ref_element: The value of the element where we'll insert data
17+
:param Any new_elements: A list of new elements to insert, or a single element
18+
:param Boolean insert_before: If True, will insert before ref_element.
19+
"""
20+
new_elements_converted = []
21+
if isinstance(new_elements, (list, tuple, set)):
22+
for i, element in enumerate(new_elements):
23+
if not isinstance(element, DoublyLinkedListElement):
24+
new_element_converted = DoublyLinkedListElement(element)
25+
if i != 0:
26+
new_element_converted.prev_element = new_elements_converted[
27+
i - 1
28+
]
29+
new_element_converted.prev_element.next_element = (
30+
new_element_converted
31+
)
32+
else:
33+
new_element_converted = element
34+
if i != 0:
35+
new_element_converted.prev_element = new_elements_converted[
36+
i - 1
37+
]
38+
new_element_converted.prev_element.next_element = (
39+
new_element_converted
40+
)
41+
new_elements_converted.append(new_element_converted)
42+
self.elements[new_element_converted.item] = new_element_converted
43+
else:
44+
if not isinstance(new_elements, DoublyLinkedListElement):
45+
new_element_converted = DoublyLinkedListElement(new_elements)
46+
else:
47+
new_element_converted = new_elements
48+
new_elements_converted.append(new_element_converted)
49+
self.elements[new_element_converted.item] = new_element_converted
50+
51+
if self.start_element == None:
52+
self.start_element = new_elements_converted[0]
53+
for pos, element in enumerate(new_elements_converted):
54+
element.prev_element = new_elements_converted[pos - 1]
55+
element.next_element = new_elements_converted[pos + 1]
56+
57+
if not self.is_cycle:
58+
new_elements_converted[0].prev_element = None
59+
new_elements_converted[-1].next_element = None
60+
else:
61+
if isinstance(ref_element, DoublyLinkedListElement):
62+
cursor = ref_element
63+
else:
64+
cursor = self.find(ref_element)
65+
66+
if insert_before:
67+
new_elements_converted[0].prev_element = cursor.prev_element
68+
new_elements_converted[-1].next_element = cursor
69+
70+
if cursor.prev_element is not None:
71+
cursor.prev_element.next_element = new_elements_converted[0]
72+
cursor.prev_element = new_elements_converted[-1]
73+
if self.start_element == cursor:
74+
self.start_element = new_elements_converted[0]
75+
else:
76+
new_elements_converted[0].prev_element = cursor
77+
new_elements_converted[-1].next_element = cursor.next_element
78+
if cursor.next_element is not None:
79+
cursor.next_element.prev_element = new_elements_converted[-1]
80+
cursor.next_element = new_elements_converted[0]
81+
82+
def append(self, new_element):
83+
"""
84+
Appends an element in the list
85+
86+
:param Any new_element: The new element to insert
87+
:param Boolean insert_before: If True, will insert before ref_element.
88+
"""
89+
if not isinstance(new_element, DoublyLinkedListElement):
90+
new_element = DoublyLinkedListElement(new_element)
91+
92+
self.elements[new_element.item] = new_element
93+
94+
if self.start_element is None:
95+
self.start_element = new_element
96+
if self.is_cycle:
97+
new_element.next_element = new_element
98+
new_element.prev_element = new_element
99+
else:
100+
if self.is_cycle:
101+
cursor = self.start_element.prev_element
102+
else:
103+
cursor = self.start_element
104+
while cursor.next_element is not None:
105+
if self.is_cycle and cursor.next_element == self.start_element:
106+
break
107+
cursor = cursor.next_element
108+
109+
new_element.prev_element = cursor
110+
new_element.next_element = cursor.next_element
111+
if cursor.next_element is not None:
112+
cursor.next_element.prev_element = new_element
113+
cursor.next_element = new_element
114+
115+
def traverse(self, start, end=None):
116+
"""
117+
Gets items based on their values
118+
119+
:param Any start: The start element
120+
:param Any stop: The end element
121+
"""
122+
output = []
123+
if self.start_element is None:
124+
return []
125+
126+
if not isinstance(start, DoublyLinkedListElement):
127+
start = self.find(start)
128+
cursor = start
129+
130+
if not isinstance(end, DoublyLinkedListElement):
131+
end = self.find(end)
132+
133+
while cursor is not None:
134+
if cursor == end:
135+
break
136+
137+
output.append(cursor)
138+
139+
cursor = cursor.next_element
140+
141+
if self.is_cycle and cursor == start:
142+
break
143+
144+
return output
145+
146+
def delete_by_value(self, to_delete):
147+
"""
148+
Deletes a given element from the list
149+
150+
:param Any to_delete: The element to delete
151+
"""
152+
output = []
153+
if self.start_element is None:
154+
return
155+
156+
cursor = to_delete
157+
cursor.prev_element.next_element = cursor.next_element
158+
cursor.next_element.prev_element = cursor.prev_element
159+
160+
def delete_by_position(self, to_delete):
161+
"""
162+
Deletes a given element from the list
163+
164+
:param Any to_delete: The element to delete
165+
"""
166+
output = []
167+
if self.start_element is None:
168+
return
169+
170+
if not isinstance(to_delete, int):
171+
raise TypeError("Position must be an integer")
172+
173+
cursor = self.start_element
174+
i = -1
175+
while cursor is not None and i < to_delete:
176+
i += 1
177+
if i == to_delete:
178+
if cursor.prev_element:
179+
cursor.prev_element.next_element = cursor.next_element
180+
if cursor.next_element:
181+
cursor.next_element.prev_element = cursor.prev_element
182+
183+
if self.start_element == cursor:
184+
self.start_element = cursor.next_element
185+
186+
del cursor
187+
return True
188+
189+
raise ValueError("Element not in list")
190+
191+
def find(self, needle):
192+
"""
193+
Finds a given item based on its value
194+
195+
:param Any needle: The element to search
196+
"""
197+
if isinstance(needle, DoublyLinkedListElement):
198+
return needle
199+
else:
200+
if needle in self.elements:
201+
return self.elements[needle]
202+
else:
203+
return False
204+
205+
206+
class DoublyLinkedListElement:
207+
def __init__(self, data, prev_element=None, next_element=None):
208+
self.item = data
209+
self.prev_element = prev_element
210+
self.next_element = next_element
211+
212+
def __repr__(self):
213+
output = [self.item]
214+
if self.prev_element is not None:
215+
output.append(self.prev_element.item)
216+
else:
217+
output.append(None)
218+
if self.next_element is not None:
219+
output.append(self.next_element.item)
220+
else:
221+
output.append(None)
222+
return str(tuple(output))

0 commit comments

Comments
 (0)