-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlinked_list.py
More file actions
322 lines (272 loc) · 8.78 KB
/
linked_list.py
File metadata and controls
322 lines (272 loc) · 8.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
"""
Data Structures and Algorithms
A brief overview of LinkedList using Python. Below are the methods implemented
1. pop
2. pop_first
3. get
4. set_value
5. append
6. prepend
7. insert
8. remove
9. reverse
By: Amitabh Suman (amitabhsuman.ss89@gmail.com)
"""
from typing import Union
# pylint: disable=too-few-public-methods
class Node:
"""
Class for creating a new node
Seperate class for SRP : Single Responsibility Principle
"""
def __init__(self, value):
self.value = value
self.next = None
class LinkedList:
"""
Class that contains all the functionality related to a Linked List
1. Create Head and Tail of linked list
2. Method to append new node
3. Method to pop from end
4. Method to pop from first
5. Method to prepend
"""
def __init__(self, value: int) -> None:
"""
Creates a new node and inits head, tail and length of the new Linked List
"""
new_node = Node(value)
self.head = new_node
self.tail = new_node
self.length = 1
def print_list(self):
"""
Prints the list whenever called
"""
temp = self.head
while temp is not None:
print(temp.value)
temp = temp.next
print("\n\n")
def get(self, index: int) -> Union[None, Node]:
"""
Get the node at index from the linked list
3 conditions are checked here is
1. If thr index passes is valid
2. If the index asked is 0 (not entirely necessary though)
3. If its a index within range
"""
if index < 0 or index > self.length:
return None
if index == 0:
return self.head
temp = self.head
for _ in range(index):
temp = temp.next
return temp
def set_value(self, index: int, value: int) -> bool:
"""
Set value of certain index to the passed on value
"""
temp = self.get(index)
if temp:
temp.value = value
return True
return False
def append(self, value: int) -> bool:
"""
Create a new node and appends to endof the list
Checks if the list was empty. If so, assigns head and tail with new node
Progresses the tail to point to the newly created node
Return True (optional return)
"""
new_node = Node(value)
if self.head is None:
self.head = new_node
self.tail = new_node
else:
self.tail.next = new_node
self.tail = new_node
self.length += 1
return True
# pylint: disable=inconsistent-return-statements
def pop(self) -> Union[None, Node]:
"""
Pops the element from end of the list.
Conditions checked are:
1. Check if linkedlist is empty, return None
2. If not 1 : take 2 variables
1. pre and temp
2. pre is temp to start with
3. While temp.next is not none, in loop : assign pre = temp and temp = temp.next
4. Once loop exits (O(n)) : pre points to last temp, before the None
(temp points to this node)
5. Assign tail to pre and tail.next to none
6. Reduce the length by 1
"""
# check if we have empty case
if self.head is None:
self.tail = None
print("Noting to pop")
return None
# remove the last element
if self.length == 0:
return None
temp = self.head
pre = self.head
while temp.next:
pre = temp
temp = temp.next
self.tail = pre
self.tail.next = None
self.length -= 1
if self.head == self.tail:
print(f"Popped value : {self.head.value}")
self.head = None
self.tail = None
return
print(f"Removed value : {temp} : value : {temp.value}")
return temp
def prepend(self, value: int) -> None:
"""
1. Create a new node.
2. Checks if the list is none.
3. If 2 : then assign head and tail to new node and tail.next to none
4. if not 2 : store head to a pre variable. Assign head to new node
5. head.next to pre
6. Increase lenght by 1
"""
new_node = Node(value)
if self.head is None:
self.head = new_node
self.tail = new_node
self.tail.next = None
if self.head:
pre = self.head
self.head = new_node
self.head.next = pre
self.length += 1
# pylint: disable=inconsistent-return-statements
def pop_first(self) -> Union[None, Node]:
"""
1. Checks if the list is none, return None
2. If len of list is 1 : make head and tail as none and lenght as 0
3. If length is more than 1 : Save head in a temp variable.
4. Make head = head.next
5. Make temp.next = None
6. Decrease lenght by 1
"""
if self.head is None:
return None
if self.length == 1:
self.head = None
self.tail = None
self.length = 0
return None
if self.length > 1:
temp = self.head
self.head = self.head.next
self.length -= 1
temp.next = None
print(f"Popped {temp} : {temp.value}")
return temp
def insert(self, index: int, value: int) -> Union[bool, None, Node]:
"""
Function to insert a value at given index.
Few conditions that can be checked for implementation
1. Check if insert is right at the begining or end
2. If no, the traverse till the index and insert
"""
if index > self.length or index < 0:
print("Index out of range")
return False
if index == 0:
# Check if the index is 0. If so, invoke the prepend method
# which will check for the status of the list
# Performs checks related to Empty list and so on.
return self.prepend(value)
if index == self.length:
# User intends to insert at end of the list.
# Used the append function
return self.append(value)
new_node = Node(value)
temp = self.get(index - 1)
new_node.next = temp.next
temp.next = new_node
self.length += 1
return True
def remove(self, index: int) -> Union[None, Node]:
"""
Remove by index. Things we have considered here are:
1. Check for index validity
2. If we have index at 0 or at end, we have methods to remove them already
3. If index is in range, we remove it by retriving the index-1 node and applying as below.
"""
if index < 0 or index > self.length:
return None
if index == 0:
self.pop_first()
if index == self.length - 1:
self.pop()
prev = self.get(index - 1)
# Since get is O(n), using prev to retrieve the temp value
temp = prev.next
prev.next = temp.next
temp.next = None
self.length -= 1
return temp
def reverse(self) -> None:
"""
Most common DSA question. The order of the code matters here a lot.
So small explanation as below
1. First reverse the Head and Tail node
2. Take 2 variables: before and after
3. before is none and after is temp.next
4. Iterate through the linked list and in below order, reverse the list
5. First after = temp.next
6. Then temp.next = before
7. Then before = temp
8. Then temp = after
"""
temp = self.head
self.head = self.tail
self.tail = temp
after = None
before = None
for _ in range(self.length):
after = temp.next
temp.next = before
before = temp
temp = after
# Try below
j = LinkedList(0)
j.print_list()
j.append(1)
j.print_list()
j.append(2)
j.print_list()
j.append(3)
j.print_list()
j.append(4)
j.print_list()
print("Checking Pop")
j.pop()
j.print_list()
print("Checking Prepend")
j.prepend(10)
j.print_list()
print("Checking Pop")
j.pop_first()
j.print_list()
print("Checking Insert")
j.insert(2, 10)
j.print_list()
print("Checking Get")
j.get(2)
j.print_list()
print("Checking Set")
j.set_value(2, 4)
j.print_list()
print("Checking Remove")
j.remove(2)
j.print_list()