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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy