Skip to content
Open
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
* [Permutations](data_structures/arrays/permutations.py)
* [Prefix Sum](data_structures/arrays/prefix_sum.py)
* [Product Sum](data_structures/arrays/product_sum.py)
* [Rotate Array](data_structures/arrays/rotate_array.py)
* [Sparse Table](data_structures/arrays/sparse_table.py)
* [Sudoku Solver](data_structures/arrays/sudoku_solver.py)
* Binary Tree
Expand Down Expand Up @@ -270,6 +271,7 @@
* [From Sequence](data_structures/linked_list/from_sequence.py)
* [Has Loop](data_structures/linked_list/has_loop.py)
* [Is Palindrome](data_structures/linked_list/is_palindrome.py)
* [Merge Sort Linked List](data_structures/linked_list/merge_sort_linked_list.py)
* [Merge Two Lists](data_structures/linked_list/merge_two_lists.py)
* [Middle Element Of Linked List](data_structures/linked_list/middle_element_of_linked_list.py)
* [Print Reverse](data_structures/linked_list/print_reverse.py)
Expand Down
155 changes: 155 additions & 0 deletions data_structures/linked_list/merge_sort_linked_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
class Node:
"""
A class representing a node in a linked list.

Attributes:
data (int): The data stored in the node.
next (Node | None): A reference to the next node in the linked list.
"""

def __init__(self, data: int) -> None:
self.data = data
self.next: Node | None = None


def get_middle(head: Node | None) -> Node | None:
"""
Find the node before the middle of the linked list
using the slow and fast pointer technique.

Parameters:
head: The head node of the linked list.

Returns:
The node before the middle of the linked list,
or None if the list has fewer than 2 nodes.

Example:
>>> head = Node(1)
>>> head.next = Node(2)
>>> head.next.next = Node(3)
>>> middle = get_middle(head)
>>> middle.data
2
"""
if head is None or head.next is None:
return None

slow: Node | None = head
fast: Node | None = head.next

while fast is not None and fast.next is not None:
if slow is None:
return None
slow = slow.next
fast = fast.next.next

return slow


def print_linked_list(head: Node | None) -> None:
"""
Print the linked list in a single line.

Parameters:
head: The head node of the linked list.

Example:
>>> head = Node(1)
>>> head.next = Node(2)
>>> head.next.next = Node(3)
>>> print_linked_list(head)
1 2 3
"""

current = head
first = True # To avoid printing space before the first element
while current:
if not first:
print(" ", end="")
print(current.data, end="")
first = False
current = current.next
print()


def merge(left: Node | None, right: Node | None) -> Node | None:
"""
Merge two sorted linked lists into one sorted linked list.

Parameters:
left: The head of the first sorted linked list.
right: The head of the second sorted linked list.

Returns:
The head of the merged sorted linked list.

Example:
>>> left = Node(1)
>>> left.next = Node(3)
>>> right = Node(2)
>>> right.next = Node(4)
>>> merged = merge(left, right)
>>> print_linked_list(merged)
1 2 3 4
"""

if left is None:
return right
if right is None:
return left

if left.data <= right.data:
result = left
result.next = merge(left.next, right)
else:
result = right
result.next = merge(left, right.next)

return result


def merge_sort_linked_list(head: Node | None) -> Node | None:
"""
Sort a linked list using the Merge Sort algorithm.

Parameters:
head: The head node of the linked list to be sorted.

Returns:
The head node of the sorted linked list.

Example:
>>> head = Node(4)
>>> head.next = Node(2)
>>> head.next.next = Node(1)
>>> head.next.next.next = Node(3)
>>> sorted_head = merge_sort_linked_list(head)
>>> print_linked_list(sorted_head)
1 2 3 4
"""

# Base Case: 0 or 1 node
if head is None or head.next is None:
return head

# Split the linked list into two halves
middle = get_middle(head)
if middle is None or middle.next is None:
return head

next_to_middle = middle.next
middle.next = None # Split the list into two parts

# Recursively sort both halves
left = merge_sort_linked_list(head)
right = merge_sort_linked_list(next_to_middle)

# Merge sorted halves
return merge(left, right)


if __name__ == "__main__":
import doctest

doctest.testmod()