From 121bfdae5f81346c8ccdd9088123cd314f3232e3 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Wed, 2 Oct 2024 15:49:25 +0530 Subject: [PATCH 01/15] Add Merge Sort Linked List algorithm --- .../linked_list/merge_sort_linked_list.py | 168 ++++++++++++++++++ 1 file changed, 168 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..d883eabb0025 --- /dev/null +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -0,0 +1,168 @@ +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. + + >>> 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 + """ + + def __init__(self, data: int): + self.data = data + self.next: Node | None = None + + +def get_middle(head: Node) -> Node: + """ + Find the middle node of the linked list using the slow and fast pointer technique. + + Parameters: + head: The head node of the linked list. + + Returns: + The middle node of the linked list. + + Example: + >>> head = Node(1) + >>> head.next = Node(2) + >>> head.next.next = Node(3) + >>> get_middle(head).data + 2 + """ + + if head is None: + return head + + slow = head # one node at a time + fast = head # two nodes at a time + while fast.next and fast.next.next: + slow = slow.next + fast = fast.next.next + return slow + + +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 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_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 + + >>> head = Node(1) + >>> head.next = Node(2) + >>> head.next.next = Node(3) + >>> head.next.next.next = Node(4) + >>> sorted_head = merge_sort_linked_list(head) + >>> print_linked_list(sorted_head) + 1 2 3 4 + + >>> head = Node(10) + >>> head.next = Node(3) + >>> head.next.next = Node(5) + >>> head.next.next.next = Node(1) + >>> sorted_head = merge_sort_linked_list(head) + >>> print_linked_list(sorted_head) + 1 3 5 10 + """ + + # 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) + next_to_middle = middle.next + middle.next = None # Split the list into two parts + + left = merge_sort_linked_list(head) # Sort the left half + right = merge_sort_linked_list(next_to_middle) # Sort the right half + sorted_list = merge(left, right) # Merge the sorted halves + return sorted_list + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 7ee4af8329a0d4c0cb64716edf7411d6aa28e895 Mon Sep 17 00:00:00 2001 From: Hardvan Date: Wed, 2 Oct 2024 10:19:43 +0000 Subject: [PATCH 02/15] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 955001e2aa23..3cff41b00ca8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -261,6 +261,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) From 809e74b7f265a84bc421e79869572faaa79bec8c Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:28:01 +0530 Subject: [PATCH 03/15] Fix comments --- data_structures/linked_list/merge_sort_linked_list.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index d883eabb0025..ef8fd982add7 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -156,9 +156,9 @@ def merge_sort_linked_list(head: Node | None) -> Node | None: next_to_middle = middle.next middle.next = None # Split the list into two parts - left = merge_sort_linked_list(head) # Sort the left half - right = merge_sort_linked_list(next_to_middle) # Sort the right half - sorted_list = merge(left, right) # Merge the sorted halves + left = merge_sort_linked_list(head) # Sort left half + right = merge_sort_linked_list(next_to_middle) # Sort right half + sorted_list = merge(left, right) # Merge sorted halves return sorted_list From 4c53ee9d75c682e8f8218cb45ded7ca2f4ebeb28 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:31:05 +0530 Subject: [PATCH 04/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index ef8fd982add7..d7ead80f94e8 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -158,7 +158,13 @@ def merge_sort_linked_list(head: Node | None) -> Node | None: left = merge_sort_linked_list(head) # Sort left half right = merge_sort_linked_list(next_to_middle) # Sort right half - sorted_list = merge(left, right) # Merge sorted halves + + # Merge sorted halves + if left is None: + return right + if right is None: + return left + sorted_list = merge(left, right) return sorted_list From 42a1f27126be9cc1c36942cc3fd7a70db24d4671 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:32:07 +0530 Subject: [PATCH 05/15] Add return type hint --- data_structures/linked_list/merge_sort_linked_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index d7ead80f94e8..844bbae909a1 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -15,7 +15,7 @@ class Node: 1 2 3 4 """ - def __init__(self, data: int): + def __init__(self, data: int) -> None: self.data = data self.next: Node | None = None From b3304eb0fdf8f07ff01d2b9d80cd5b351ff0301d Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:33:17 +0530 Subject: [PATCH 06/15] Fix type hint --- data_structures/linked_list/merge_sort_linked_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index 844bbae909a1..7e93b63a43e7 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -20,7 +20,7 @@ def __init__(self, data: int) -> None: self.next: Node | None = None -def get_middle(head: Node) -> Node: +def get_middle(head: Node | None) -> Node | None: """ Find the middle node of the linked list using the slow and fast pointer technique. From 81d2d511d888708332d82c6a980556f74516d2b7 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:34:21 +0530 Subject: [PATCH 07/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index 7e93b63a43e7..844bbae909a1 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -20,7 +20,7 @@ def __init__(self, data: int) -> None: self.next: Node | None = None -def get_middle(head: Node | None) -> Node | None: +def get_middle(head: Node) -> Node: """ Find the middle node of the linked list using the slow and fast pointer technique. From 62116938330b2625237391a00ea80b1d46a99ee1 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:35:54 +0530 Subject: [PATCH 08/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index 844bbae909a1..7eb34bbbcc61 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -160,10 +160,6 @@ def merge_sort_linked_list(head: Node | None) -> Node | None: right = merge_sort_linked_list(next_to_middle) # Sort right half # Merge sorted halves - if left is None: - return right - if right is None: - return left sorted_list = merge(left, right) return sorted_list From 5556acb7f88d8740ea27b6edcc98afac9309be85 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:37:38 +0530 Subject: [PATCH 09/15] Fix --- .../linked_list/merge_sort_linked_list.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index 7eb34bbbcc61..c06041787706 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -20,7 +20,7 @@ def __init__(self, data: int) -> None: self.next: Node | None = None -def get_middle(head: Node) -> Node: +def get_middle(head: Node | None) -> Node | None: """ Find the middle node of the linked list using the slow and fast pointer technique. @@ -28,17 +28,20 @@ def get_middle(head: Node) -> Node: head: The head node of the linked list. Returns: - The middle node of the linked list. + The middle node of the linked list, or None if the list is empty. Example: >>> head = Node(1) >>> head.next = Node(2) >>> head.next.next = Node(3) - >>> get_middle(head).data + >>> middle = get_middle(head) + >>> middle.data if middle else None 2 + >>> get_middle(None) is None + True """ - if head is None: + if head is None or head.next is None: return head slow = head # one node at a time @@ -153,6 +156,8 @@ def merge_sort_linked_list(head: Node | None) -> Node | None: # Split the linked list into two halves middle = get_middle(head) + if middle is None: + return head next_to_middle = middle.next middle.next = None # Split the list into two parts From 4aa953e4002d2e37e7e1cca45845111d2c28c4b4 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:39:31 +0530 Subject: [PATCH 10/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index c06041787706..9bebf0b5c162 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -47,7 +47,7 @@ def get_middle(head: Node | None) -> Node | None: slow = head # one node at a time fast = head # two nodes at a time while fast.next and fast.next.next: - slow = slow.next + slow: Node | None = slow.next fast = fast.next.next return slow From 62d24124826410b40bde8e25f8692c22508f137e Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:40:56 +0530 Subject: [PATCH 11/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index 9bebf0b5c162..b8ba5d200195 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -44,10 +44,10 @@ def get_middle(head: Node | None) -> Node | None: if head is None or head.next is None: return head - slow = head # one node at a time + slow: Node | None = head # one node at a time fast = head # two nodes at a time while fast.next and fast.next.next: - slow: Node | None = slow.next + slow = slow.next fast = fast.next.next return slow From d2b3c1d11a9a1f8f3a99381fdabe04852ed40bd5 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:43:51 +0530 Subject: [PATCH 12/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index b8ba5d200195..ec8ce7f8937a 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -44,11 +44,12 @@ def get_middle(head: Node | None) -> Node | None: if head is None or head.next is None: return head - slow: Node | None = head # one node at a time - fast = head # two nodes at a time - while fast.next and fast.next.next: + slow: Node | None = head + fast: Node | None = head + while fast is not None and fast.next is not None: slow = slow.next fast = fast.next.next + return slow From fb13521086f3c9f64060c34206a5c67d9af54a21 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:45:03 +0530 Subject: [PATCH 13/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index ec8ce7f8937a..f72fe3ee81fd 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -47,6 +47,8 @@ def get_middle(head: Node | None) -> Node | None: slow: Node | None = head fast: Node | None = head while fast is not None and fast.next is not None: + if slow is None: # This should never happen, but it satisfies the type checker + return None slow = slow.next fast = fast.next.next From 1ac1f0c0e4d63e92951e617470913c25e6a2009c Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:54:07 +0530 Subject: [PATCH 14/15] Fix doctests --- .../linked_list/merge_sort_linked_list.py | 107 +++++++----------- 1 file changed, 41 insertions(+), 66 deletions(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index f72fe3ee81fd..deaab02d4bd5 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -5,14 +5,6 @@ class Node: Attributes: data (int): The data stored in the node. next (Node | None): A reference to the next node in the linked list. - - >>> 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 """ def __init__(self, data: int) -> None: @@ -22,39 +14,63 @@ def __init__(self, data: int) -> None: def get_middle(head: Node | None) -> Node | None: """ - Find the middle node of the linked list using the slow and fast pointer technique. + 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 middle node of the linked list, or None if the list is empty. + 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 if middle else None + >>> middle.data 2 - >>> get_middle(None) is None - True """ - if head is None or head.next is None: - return head + return None + + slow: Node = head + fast: Node | None = head.next - slow: Node | None = head - fast: Node | None = head while fast is not None and fast.next is not None: - if slow is None: # This should never happen, but it satisfies the type checker - 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. @@ -91,32 +107,6 @@ def merge(left: Node | None, right: Node | None) -> Node | None: return result -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_sort_linked_list(head: Node | None) -> Node | None: """ Sort a linked list using the Merge Sort algorithm. @@ -135,22 +125,6 @@ def merge_sort_linked_list(head: Node | None) -> Node | None: >>> sorted_head = merge_sort_linked_list(head) >>> print_linked_list(sorted_head) 1 2 3 4 - - >>> head = Node(1) - >>> head.next = Node(2) - >>> head.next.next = Node(3) - >>> head.next.next.next = Node(4) - >>> sorted_head = merge_sort_linked_list(head) - >>> print_linked_list(sorted_head) - 1 2 3 4 - - >>> head = Node(10) - >>> head.next = Node(3) - >>> head.next.next = Node(5) - >>> head.next.next.next = Node(1) - >>> sorted_head = merge_sort_linked_list(head) - >>> print_linked_list(sorted_head) - 1 3 5 10 """ # Base Case: 0 or 1 node @@ -159,17 +133,18 @@ def merge_sort_linked_list(head: Node | None) -> Node | None: # Split the linked list into two halves middle = get_middle(head) - if middle is None: + 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 - left = merge_sort_linked_list(head) # Sort left half - right = merge_sort_linked_list(next_to_middle) # Sort right half + # Recursively sort both halves + left = merge_sort_linked_list(head) + right = merge_sort_linked_list(next_to_middle) # Merge sorted halves - sorted_list = merge(left, right) - return sorted_list + return merge(left, right) if __name__ == "__main__": From ed62d338705e2add7b5c3eb5ae0566745246009e Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Thu, 3 Oct 2024 11:56:04 +0530 Subject: [PATCH 15/15] Fix --- data_structures/linked_list/merge_sort_linked_list.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data_structures/linked_list/merge_sort_linked_list.py b/data_structures/linked_list/merge_sort_linked_list.py index deaab02d4bd5..68727685e34d 100644 --- a/data_structures/linked_list/merge_sort_linked_list.py +++ b/data_structures/linked_list/merge_sort_linked_list.py @@ -35,10 +35,12 @@ def get_middle(head: Node | None) -> Node | None: if head is None or head.next is None: return None - slow: Node = head + 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