Skip to content

Commit 28386d0

Browse files
committed
feat(dsa): remove nth last node from linked list
1 parent 7b9b37f commit 28386d0

File tree

3 files changed

+191
-6
lines changed

3 files changed

+191
-6
lines changed

datastructures/linked_lists/__init__.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@ def __iter__(self):
4545
return
4646

4747
if current:
48-
yield current.data
48+
if current.data:
49+
yield current.data
4950

5051
if current.next:
5152
node = current.next
5253
while node:
53-
yield node.data
54+
if node.data:
55+
yield node.data
5456
node = node.next
5557

5658
@abstractmethod
@@ -287,7 +289,7 @@ def delete_node_at_position(self, position: int):
287289
:param position: Position of node to delete
288290
"""
289291
if not 0 <= position <= len(self) - 1:
290-
raise ValueError("Position out of bounds")
292+
raise ValueError(f"Position ${position} out of bounds")
291293

292294
if self.head is None:
293295
return None
@@ -323,6 +325,21 @@ def delete_middle_node(self) -> Optional[Node]:
323325
"""
324326
raise NotImplementedError("Not yet implemented")
325327

328+
@abstractmethod
329+
def delete_nth_last_node(self, n: int) -> Optional[Node]:
330+
"""
331+
Deletes the nth last node of the linked list and returns the head of the linked list.
332+
Example:
333+
n = 1
334+
43 -> 68 -> 11 -> 5 -> 69 -> 37 -> 70 -> None
335+
43 -> 68 -> 11 -> 5 -> 69 -> 37 -> None
336+
Args:
337+
n (int): the position from the last node of the node to delete
338+
Returns:
339+
Node: Head of the linked list
340+
"""
341+
raise NotImplementedError("Not yet implemented")
342+
326343
@abstractmethod
327344
def display(self):
328345
"""

datastructures/linked_lists/singly_linked_list/__init__.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ def delete_node_at_position(self, position: int) -> Optional[SingleNode]:
134134
current = current.next
135135

136136
if current is None:
137-
raise ValueError("Invalid position found, reached end of list")
137+
raise ValueError(f"Invalid position {position} found, reached end of list")
138138

139-
current.data = current.next.data
140-
current.next = current.next.next
139+
current.data = current.next.data if current.next else None
140+
current.next = current.next.next if current.next else None
141141
return self.head
142142

143143
def delete_node(self, single_node: SingleNode):
@@ -251,6 +251,21 @@ def delete_middle_node_2_pointers(self) -> Optional[SingleNode]:
251251

252252
return middle_node
253253

254+
def delete_nth_last_node(self, n: int) -> Optional[Node]:
255+
length_of_linked_list = len(self)
256+
257+
if not 1 <= n <= length_of_linked_list:
258+
raise ValueError(f"Position {n} is out of bounds")
259+
260+
if not self.head.next:
261+
return self.head
262+
263+
position_of_node_to_delete = length_of_linked_list - n
264+
265+
self.delete_node_at_position(position_of_node_to_delete)
266+
267+
return self.head
268+
254269
def shift(self):
255270
"""
256271
Since this is a singly linked list, this will have to make the head's next to the position of head
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import unittest
2+
from . import SinglyLinkedList
3+
4+
5+
class RemoveNthLastNodeTestCases(unittest.TestCase):
6+
7+
def test_0(self):
8+
"""should raise ValueError when the value of n is out of bounds"""
9+
linked_list = SinglyLinkedList()
10+
data = [43, 68, 11, 5, 69, 37, 70]
11+
for d in data:
12+
linked_list.append(d)
13+
14+
with self.assertRaises(ValueError):
15+
linked_list.delete_nth_last_node(0)
16+
17+
with self.assertRaises(ValueError):
18+
linked_list.delete_nth_last_node(8)
19+
20+
def test_1(self):
21+
"""should delete nth last node where n=1 and list is 43 -> 68 -> 11 -> 5 -> 69 -> 37 -> 70 to become 43 -> 68 -> 11 -> 5 -> 69 -> 37"""
22+
linked_list = SinglyLinkedList()
23+
data = [43, 68, 11, 5, 69, 37, 70]
24+
for d in data:
25+
linked_list.append(d)
26+
27+
new_head = linked_list.delete_nth_last_node(1)
28+
expected = [43, 68, 11, 5, 69, 37]
29+
30+
self.assertIsNotNone(new_head)
31+
self.assertEqual(new_head.data, 43)
32+
self.assertEqual(expected, list(linked_list))
33+
34+
def test_2(self):
35+
"""should delete nth last node where n=3 and list is 23 -> 28 -> 10 -> 5 -> 67 -> 39 -> 70 to become 23 -> 28 -> 10 -> 5 -> 39 -> 70"""
36+
linked_list = SinglyLinkedList()
37+
data = [23, 28, 10, 5, 67, 39, 70]
38+
for d in data:
39+
linked_list.append(d)
40+
41+
new_head = linked_list.delete_nth_last_node(3)
42+
expected = [23, 28, 10, 5, 39, 70]
43+
44+
self.assertIsNotNone(new_head)
45+
self.assertEqual(new_head.data, 23)
46+
self.assertEqual(expected, list(linked_list))
47+
48+
def test_3(self):
49+
"""should delete nth last node where n=5 and list is 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 to become 1 -> 2 -> 4 -> 5 -> 6 -> 7"""
50+
linked_list = SinglyLinkedList()
51+
data = [1, 2, 3, 4, 5, 6, 7]
52+
for d in data:
53+
linked_list.append(d)
54+
55+
new_head = linked_list.delete_nth_last_node(5)
56+
expected = [1, 2, 4, 5, 6, 7]
57+
58+
self.assertIsNotNone(new_head)
59+
self.assertEqual(new_head.data, 1)
60+
self.assertEqual(expected, list(linked_list))
61+
62+
def test_4(self):
63+
"""should delete nth last node where n=4 and list is 50 -> 40 -> 30 -> 20 to become 40 -> 30 -> 20"""
64+
linked_list = SinglyLinkedList()
65+
data = [50, 40, 30, 20]
66+
for d in data:
67+
linked_list.append(d)
68+
69+
new_head = linked_list.delete_nth_last_node(4)
70+
expected = [40, 30, 20]
71+
72+
self.assertIsNotNone(new_head)
73+
self.assertEqual(new_head.data, 40)
74+
self.assertEqual(expected, list(linked_list))
75+
76+
def test_5(self):
77+
"""should delete nth last node where n=6 and list is [69,8,49,106,116,112] to become [8,49,106,116,112]"""
78+
linked_list = SinglyLinkedList()
79+
data = [69,8,49,106,116,112]
80+
for d in data:
81+
linked_list.append(d)
82+
83+
n = 6
84+
new_head = linked_list.delete_nth_last_node(n)
85+
expected = [8,49,106,116,112]
86+
87+
self.assertIsNotNone(new_head)
88+
self.assertEqual(new_head.data, 8)
89+
self.assertEqual(expected, list(linked_list))
90+
91+
def test_6(self):
92+
"""should delete nth last node where n=1 and list is 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 to become 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8"""
93+
linked_list = SinglyLinkedList()
94+
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
95+
for d in data:
96+
linked_list.append(d)
97+
98+
n = 1
99+
new_head = linked_list.delete_nth_last_node(n)
100+
expected = [1, 2, 3, 4, 5, 6, 7, 8]
101+
102+
self.assertIsNotNone(new_head)
103+
self.assertEqual(new_head.data, 1)
104+
self.assertEqual(expected, list(linked_list))
105+
106+
def test_7(self):
107+
"""should delete nth last node where n=4 and list is [288,224,275,390,4,383,330,60,193] to become [288,224,275,390,4,330,60,193]"""
108+
linked_list = SinglyLinkedList()
109+
data = [288,224,275,390,4,383,330,60,193]
110+
for d in data:
111+
linked_list.append(d)
112+
113+
n = 4
114+
new_head = linked_list.delete_nth_last_node(n)
115+
expected = [288,224,275,390,4,330,60,193]
116+
117+
self.assertIsNotNone(new_head)
118+
self.assertEqual(new_head.data, 288)
119+
self.assertEqual(expected, list(linked_list))
120+
121+
def test_8(self):
122+
"""should delete nth last node where n=3 and list is [34,53,6,95,38,28,17,63,16,76] to become [34,53,6,95,38,28,17,16,76]"""
123+
linked_list = SinglyLinkedList()
124+
data = [34,53,6,95,38,28,17,63,16,76]
125+
for d in data:
126+
linked_list.append(d)
127+
128+
n = 3
129+
new_head = linked_list.delete_nth_last_node(n)
130+
expected = [34,53,6,95,38,28,17,16,76]
131+
132+
self.assertIsNotNone(new_head)
133+
self.assertEqual(new_head.data, 34)
134+
self.assertEqual(expected, list(linked_list))
135+
136+
def test_9(self):
137+
"""should delete nth last node where n=2 and list is [23,28,10,5,67,39,70,28] to become [23,28,10,5,67,39,28]"""
138+
linked_list = SinglyLinkedList()
139+
data = [23,28,10,5,67,39,70,28]
140+
for d in data:
141+
linked_list.append(d)
142+
143+
n = 2
144+
new_head = linked_list.delete_nth_last_node(n)
145+
expected = [23,28,10,5,67,39,28]
146+
147+
self.assertIsNotNone(new_head)
148+
self.assertEqual(new_head.data, 23)
149+
self.assertEqual(expected, list(linked_list))
150+
151+
152+
if __name__ == '__main__':
153+
unittest.main()

0 commit comments

Comments
 (0)