Skip to content

Commit f58f8a2

Browse files
committed
feat(datastructures, linked-lists): reorder list
1 parent fa51bf6 commit f58f8a2

12 files changed

+329
-54
lines changed

datastructures/linked_lists/__init__.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,3 +715,36 @@ def pairs_with_sum(self, target: T) -> List[Tuple[Node, Node]]:
715715
List: list of pairs
716716
"""
717717
raise NotImplementedError("not yet implemented")
718+
719+
@staticmethod
720+
def reverse_list(head: Node) -> Optional[Node]:
721+
"""
722+
Reverses a linked list given the head node
723+
Args:
724+
head Node: the head node of the linked list
725+
Returns:
726+
Optional[Node]: the new head node of the reversed linked list
727+
"""
728+
if head is None or head.next is None:
729+
return head
730+
731+
# track previous node, so we can point our next pointer to it
732+
previous = None
733+
# track node to loop through
734+
current_node = head
735+
736+
while current_node:
737+
# track the next node to not lose it while adjusting pointers
738+
nxt = current_node.next
739+
740+
# set the next pointer to the node behind it, previous
741+
current_node.next = previous
742+
743+
# adjust the new previous node to the current node for subsequent loops
744+
previous = current_node
745+
746+
# move our node pointer up to the next node in front of it
747+
current_node = nxt
748+
749+
# return the new tail of the k-group which is our head
750+
return previous

datastructures/linked_lists/singly_linked_list/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,3 +592,29 @@ Here `n` is the number of nodes in the linked list
592592
- Stack
593593

594594
---
595+
596+
## Reorder List
597+
598+
Given the head of a singly linked list, reorder the list as if it were folded on itself. For example, if the list is
599+
represented as follows:
600+
601+
L0 -> L1 -> L2 -> L3 -> L4 -> L5
602+
603+
The reordered list should be:
604+
605+
L0 -> L5 -> L1 -> L4 -> L2 -> L3
606+
607+
You don’t need to modify the values in the list’s nodes; only the links between nodes need to be changed.
608+
609+
### Constraints
610+
611+
- The range of number of nodes in the list is [1, 500]
612+
- -5000 <= `node.value` <= 5000
613+
614+
### Examples
615+
616+
![Example 1](./images/examples/singly_linked_list_reorder_list_example_1.png)
617+
![Example 2](./images/examples/singly_linked_list_reorder_list_example_2.png)
618+
![Example 3](./images/examples/singly_linked_list_reorder_list_example_3.png)
619+
![Example 4](./images/examples/singly_linked_list_reorder_list_example_4.png)
620+
![Example 5](./images/examples/singly_linked_list_reorder_list_example_5.png)
23 KB
Loading
25.2 KB
Loading
26.2 KB
Loading
29.1 KB
Loading
31.6 KB
Loading

datastructures/linked_lists/singly_linked_list/single_linked_list.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
from datastructures.linked_lists.singly_linked_list.node import SingleNode
55
from datastructures.linked_lists import LinkedList, T, Node
66
from datastructures.linked_lists.exceptions import EmptyLinkedList
7+
from datastructures.linked_lists.singly_linked_list.single_linked_list_utils import (
8+
reverse_list,
9+
merge_and_weave,
10+
)
711

812

913
class SinglyLinkedList(LinkedList):
@@ -983,6 +987,36 @@ def reverse_list(head_node: SingleNode) -> SingleNode:
983987
def remove_tail(self):
984988
pass
985989

990+
def reorder_list(self) -> Optional[SingleNode]:
991+
"""
992+
Reorders the linked list in place.
993+
Returns:
994+
head node of reversed linked list
995+
"""
996+
# return early if there is no head node
997+
if self.head is None:
998+
return None
999+
1000+
# first split the linked list into two halves. To do this without knowing the length of the linked list beforehand
1001+
# we must first find the middle node. This uses the slow & fast pointer approach
1002+
middle_node = self.middle_node()
1003+
1004+
# Store the second half head node
1005+
second_half_head = middle_node.next
1006+
# cut the connection between the first half and the second half
1007+
middle_node.next = None
1008+
1009+
# Now, we need to reverse the second half of the linked list in place
1010+
# The reversal step involves taking the second half of the linked list and reversing it in place
1011+
# for example, if the linked list is 1 -> 2 -> 3 -> 4 -> 5, the second half is 3 -> 4 -> 5
1012+
# after reversing, it becomes 5 -> 4 -> 3
1013+
reversed_second_half = self.reverse_list(second_half_head)
1014+
1015+
# now we can merge and weave the first half and the reversed second half
1016+
reordered_list = merge_and_weave(self.head, reversed_second_half)
1017+
1018+
return reordered_list
1019+
9861020
def kth_to_last_node(self, k: int) -> Optional[SingleNode]:
9871021
"""
9881022
Gets the Kth to the last node.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from typing import Optional
2+
from datastructures.linked_lists.singly_linked_list.node import SingleNode
3+
4+
5+
def merge_and_weave(
6+
first_half_head: SingleNode, second_half_head: SingleNode
7+
) -> Optional[SingleNode]:
8+
"""
9+
Merges and weaves the first half and the reversed second half of the linked list in place.
10+
Args:
11+
first_half_head: head node of the first half of the linked list
12+
second_half_head: head node of the reversed second half of the linked list
13+
Returns:
14+
head node of the merged and weaved linked list
15+
"""
16+
if first_half_head is None or second_half_head is None:
17+
return None
18+
19+
p1 = first_half_head
20+
p2 = second_half_head
21+
22+
while p2:
23+
# save the pointer 1 next node to not loose it
24+
p1_next = p1.next
25+
p2_next = p2.next
26+
27+
# now we can move the pointers
28+
p1.next = p2
29+
p2.next = p1_next
30+
31+
p1, p2 = p1_next, p2_next
32+
33+
return first_half_head
34+
35+
36+
def reverse_list(head: SingleNode) -> Optional[SingleNode]:
37+
"""
38+
Reverses a linked list given the head node
39+
Args:
40+
head Node: the head node of the linked list
41+
Returns:
42+
Optional[Node]: the new head node of the reversed linked list
43+
"""
44+
if head is None or head.next is None:
45+
return head
46+
47+
# track previous node, so we can point our next pointer to it
48+
previous = None
49+
# track node to loop through
50+
current_node = head
51+
52+
while current_node:
53+
# track the next node to not lose it while adjusting pointers
54+
nxt = current_node.next
55+
56+
# set the next pointer to the node behind it, previous
57+
current_node.next = previous
58+
59+
# adjust the new previous node to the current node for subsequent loops
60+
previous = current_node
61+
62+
# move our node pointer up to the next node in front of it
63+
current_node = nxt
64+
65+
# return the new tail of the k-group which is our head
66+
return previous
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import unittest
2+
from typing import List
3+
from parameterized import parameterized
4+
from datastructures.linked_lists.singly_linked_list.single_linked_list import (
5+
SinglyLinkedList,
6+
)
7+
8+
TEST_CASES = [
9+
([1, 2, 3, 4, 5], [1, 5, 2, 4, 3]),
10+
([1, 2, 3, 4], [1, 4, 2, 3]),
11+
([1, 1, 2, 2, 3, -1, 10, 12], [1, 12, 1, 10, 2, -1, 2, 3]),
12+
([10, 20, -22, 21, -12], [10, -12, 20, 21, -22]),
13+
([1, 3, 5, 7, 9, 10, 8, 6, 4, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
14+
([1, 2, 3, 4, 5, 6], [1, 6, 2, 5, 3, 4]),
15+
(
16+
[7, 0, 10, 13, 12, 19, 1, 3, 6, 7, 4, 2, 11],
17+
[7, 11, 0, 2, 10, 4, 13, 7, 12, 6, 19, 3, 1],
18+
),
19+
([0, 8, 3, 1, 2, 7], [0, 7, 8, 2, 3, 1]),
20+
([7, 4, 6, 1, 5, 8], [7, 8, 4, 5, 6, 1]),
21+
([9, 0, 8, 2], [9, 2, 0, 8]),
22+
([6, 8, 7], [6, 7, 8]),
23+
]
24+
25+
26+
class ReorderListTestCase(unittest.TestCase):
27+
@parameterized.expand(TEST_CASES)
28+
def test_reorder_list(self, input_list: List[int], expected_output: List[int]):
29+
linked_list = SinglyLinkedList()
30+
for data in input_list:
31+
linked_list.append(data)
32+
33+
actual = linked_list.reorder_list()
34+
self.assertIsNotNone(actual)
35+
36+
# since the head node is returned, we want the values of the linked list and not just the head node
37+
# to check that the actual reordering worked
38+
actual_list: List[int] = []
39+
curr = actual
40+
while curr:
41+
actual_list.append(curr.data)
42+
curr = curr.next
43+
44+
self.assertEqual(expected_output, actual_list)
45+
46+
47+
if __name__ == "__main__":
48+
unittest.main()

0 commit comments

Comments
 (0)