From ed7219f783356fe1a79b23478ed282fa1cc6d97e Mon Sep 17 00:00:00 2001 From: F-Blaze Date: Fri, 22 May 2026 16:10:23 -0500 Subject: [PATCH 1/2] Add merge sort for linked list --- .../linked_list/merge_sort_linked_list.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 data_structures/linked_list/merge_sort_linked_list.py diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py new file mode 100644 index 000000000000..44cf5defc770 --- /dev/null +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -0,0 +1,75 @@ +""" +Merge sort for a singly linked list. + +https://en.wikipedia.org/wiki/Merge_sort +""" + +from data_structures.linked_list.singly_linked_list import Node + + +def split_middle(node: Node) -> tuple[Node | None, Node | None]: + """ + Find the middle node of a linked list using the fast/slow pointer method. + + Returns a tuple containing: + - the node before the middle + - the middle node + """ + fast = slow = node + previous: Node | None = None + + while fast and fast.next_node: + fast = fast.next_node.next_node if fast.next_node else None + previous = slow + slow = slow.next_node + + return previous, slow + + +def sort_list(head: Node | None) -> Node | None: + """ + Sort a linked list using merge sort. + """ + if head is None: + return head + + if head.next_node is None: + return head + + previous, middle_node = split_middle(head) + if previous: + previous.next_node = None + + left = head + right = middle_node + + left = sort_list(left) + right = sort_list(right) + + dummy_head = current = Node(0) + + while left and right: + if left.data < right.data: + current.next_node = left + left = left.next_node + else: + current.next_node = right + right = right.next_node + + current = current.next_node + + while left: + current.next_node = left + next_node = left.next_node + left.next_node = None + left = next_node + current = current.next_node + + while right: + current.next_node = right + next_node = right.next_node + right.next_node = None + right = next_node + current = current.next_node + + return dummy_head.next_node From 3d3443a8fd44c13c2de83f0c20225eb1e1093417 Mon Sep 17 00:00:00 2001 From: F-Blaze Date: Fri, 22 May 2026 16:26:00 -0500 Subject: [PATCH 2/2] Add doctests for linked list merge sort --- .../linked_list/merge_sort_linked_list.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index 44cf5defc770..c4ba091e5c0f 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -14,6 +14,20 @@ def split_middle(node: Node) -> tuple[Node | None, Node | None]: Returns a tuple containing: - the node before the middle - the middle node + + >>> head = Node(1) + >>> head.next_node = Node(2) + >>> head.next_node.next_node = Node(3) + >>> head.next_node.next_node.next_node = Node(4) + >>> previous, middle = split_middle(head) + >>> previous is not None + True + >>> middle is not None + True + >>> previous.data + 2 + >>> middle.data + 3 """ fast = slow = node previous: Node | None = None @@ -29,6 +43,30 @@ def split_middle(node: Node) -> tuple[Node | None, Node | None]: def sort_list(head: Node | None) -> Node | None: """ Sort a linked list using merge sort. + + >>> head = Node(4) + >>> head.next_node = Node(2) + >>> head.next_node.next_node = Node(1) + >>> head.next_node.next_node.next_node = Node(3) + >>> sorted_head = sort_list(head) + >>> sorted_head is not None + True + >>> sorted_head.data + 1 + >>> sorted_head.next_node is not None + True + >>> sorted_head.next_node.data + 2 + >>> sorted_head.next_node.next_node is not None + True + >>> sorted_head.next_node.next_node.data + 3 + >>> sorted_head.next_node.next_node.next_node is not None + True + >>> sorted_head.next_node.next_node.next_node.data + 4 + >>> sort_list(None) is None + True """ if head is None: return head @@ -73,3 +111,33 @@ def sort_list(head: Node | None) -> Node | None: current = current.next_node return dummy_head.next_node + + +def test_merge_sort() -> None: + """ + >>> test_merge_sort() + """ + head = Node(4) + head.next_node = Node(2) + head.next_node.next_node = Node(5) + head.next_node.next_node.next_node = Node(1) + head.next_node.next_node.next_node.next_node = Node(3) + + sorted_head = sort_list(head) + + assert sorted_head is not None + assert sorted_head.data == 1 + assert sorted_head.next_node is not None + assert sorted_head.next_node.data == 2 + assert sorted_head.next_node.next_node is not None + assert sorted_head.next_node.next_node.data == 3 + assert sorted_head.next_node.next_node.next_node is not None + assert sorted_head.next_node.next_node.next_node.data == 4 + assert sorted_head.next_node.next_node.next_node.next_node is not None + assert sorted_head.next_node.next_node.next_node.next_node.data == 5 + + +if __name__ == "__main__": + from doctest import testmod + + testmod()