Skip to content

Commit 362f705

Browse files
committed
feat(data structures, linked list): additional algorithm to reverse_between
1 parent de44942 commit 362f705

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

datastructures/linked_lists/singly_linked_list/single_linked_list.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,67 @@ def reverse_between(self, left: int, right: int) -> Optional[SingleNode]:
430430
tail_pointer.next = current_pointer
431431
return self.head
432432

433+
def reverse_between_with_dummy(self, left: int, right: int) -> Optional[SingleNode]:
434+
"""
435+
Reverse linked list between left & right node positions using a dummy node
436+
437+
Algorithm steps:
438+
- We initialize a dummy node, which will be helpful in scenarios where the reversal of the sublist starts from
439+
the head of the list.
440+
- We set the next node of dummy to point to the head of the list.
441+
- We initialize a pointer, prev, to the dummy node. This pointer will help us reconnect the sublist to the
442+
entire list after it has been reversed.
443+
- We use a loop to traverse the list with the prev pointer and until it reaches the node immediately before the
444+
sublist to be reversed.
445+
- We initialize a curr pointer, which points to the node next to prev.
446+
- Another loop is used to reverse the sublist. This loop iterates right - left times, which is the number of
447+
nodes in the sublist minus one:
448+
- We set next_node to curr.next, representing the node to be moved to the front of the reversed sublist.
449+
- We update curr.next to next_node.next, effectively removing next_node from its current position in the
450+
sublist.
451+
- We set next_node.next to prev.next, inserting next_node at the beginning of the reversed sublist.
452+
- We update prev.next to next_node, adjusting the pointer to next_node for the next iteration.
453+
454+
- Finally, we return dummy.next, which is the head of the modified linked list.
455+
456+
Args:
457+
left (int): the starting position
458+
right (int): the end position
459+
460+
Returns:
461+
Optional[SingleNode]: the head of the reversed linked list
462+
"""
463+
if left > right:
464+
raise ValueError(f"left {left} cannot be greater than right {right}")
465+
466+
if self.head is None or self.head.next is None:
467+
return self.head
468+
469+
if right > len(self):
470+
raise ValueError(
471+
f"right {right} cannot be greater than the length of the linked list {len(self)}"
472+
)
473+
474+
if left == right:
475+
return self.head
476+
477+
dummy = SingleNode(0)
478+
dummy.next = self.head
479+
prev = dummy
480+
481+
for _ in range(left - 1):
482+
prev = prev.next
483+
484+
curr = prev.next
485+
486+
for _ in range(right - left):
487+
next_node = curr.next
488+
curr.next = next_node.next
489+
next_node.next = prev.next
490+
prev.next = next_node
491+
492+
return dummy.next
493+
433494
def unshift(self, node_: SingleNode) -> SingleNode:
434495
if self.head:
435496
return node_

datastructures/linked_lists/singly_linked_list/test_singly_linked_revese.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,55 @@ def test_reverse_between(
7070
# perform assertion
7171
self.assertEqual(expected, actual_list)
7272

73+
@parameterized.expand(
74+
[
75+
([1, 2, 3, 4, 5, 4, 3, 2, 1], 1, 9, [1, 2, 3, 4, 5, 4, 3, 2, 1]),
76+
([1, 2, 3, 4, 5], 2, 4, [1, 4, 3, 2, 5]),
77+
([1, 2, 3, 4, 5], 1, 5, [5, 4, 3, 2, 1]),
78+
(
79+
[103, 7, 10, -9, 105, 67, 31, 63],
80+
1,
81+
8,
82+
[63, 31, 67, 105, -9, 10, 7, 103],
83+
),
84+
(
85+
[103, 7, 10, -9, 105, 67, 31, 63],
86+
1,
87+
8,
88+
[63, 31, 67, 105, -9, 10, 7, 103],
89+
),
90+
([-499, 399, -299, 199, -99, 9], 3, 5, [-499, 399, -99, 199, -299, 9]),
91+
([7, -9, 2, -10, 1, -8, 6], 2, 5, [7, 1, -10, 2, -9, -8, 6]),
92+
([6, 8, 7], 1, 2, [8, 6, 7]),
93+
([9, 0, 8, 2], 2, 4, [9, 2, 8, 0]),
94+
([7, 4, 6, 1, 5, 8], 2, 5, [7, 5, 1, 6, 4, 8]),
95+
([3, 7, 12, 2, 5, 1], 3, 6, [3, 7, 1, 5, 2, 12]),
96+
([3, 6, 7, 4, 2], 2, 4, [3, 4, 7, 6, 2]),
97+
]
98+
)
99+
def test_reverse_between_with_dummy(
100+
self, data: List[int], left: int, right: int, expected: List[int]
101+
):
102+
"""should reverse linked list between left and right arguments"""
103+
linked_list = SinglyLinkedList()
104+
# add the data to the linked list
105+
for d in data:
106+
linked_list.append(d)
107+
108+
# perform reversal
109+
actual = linked_list.reverse_between_with_dummy(left, right)
110+
111+
# since the head node is returned, we want the values of the linked list and not just the head node
112+
# to check that the actual reversal worked
113+
actual_list: List[int] = []
114+
curr = actual
115+
while curr:
116+
actual_list.append(curr.data)
117+
curr = curr.next
118+
119+
# perform assertion
120+
self.assertEqual(expected, actual_list)
121+
73122

74123
if __name__ == "__main__":
75124
unittest.main()

0 commit comments

Comments
 (0)