From 9c16d066629b04905481633a5bdd1f123f2ec22e Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 13:02:28 +0530 Subject: [PATCH 01/11] add doctest ut --- data_structures/linked_list/is_palindrome.py | 80 ++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index d540fb69f36b..dc69948d0a52 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,4 +1,29 @@ def is_palindrome(head): + """ + Check if a linked list is a palindrome. + + Args: + head (ListNode): The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome(None) + True + + >>> is_palindrome(ListNode(1)) + True + + >>> is_palindrome(ListNode(1, ListNode(2))) + False + + >>> is_palindrome(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + """ if not head: return True # split the list to two parts @@ -26,6 +51,31 @@ def is_palindrome(head): def is_palindrome_stack(head): + """ + Check if a linked list is a palindrome using a stack. + + Args: + head (ListNode): The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome_stack(None) + True + + >>> is_palindrome_stack(ListNode(1)) + True + + >>> is_palindrome_stack(ListNode(1, ListNode(2))) + False + + >>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + """ if not head or not head.next: return True @@ -50,6 +100,31 @@ def is_palindrome_stack(head): def is_palindrome_dict(head): + """ + Check if a linked list is a palindrome using a dictionary. + + Args: + head (ListNode): The head of the linked list. + + Returns: + bool: True if the linked list is a palindrome, False otherwise. + + Examples: + >>> is_palindrome_dict(None) + True + + >>> is_palindrome_dict(ListNode(1)) + True + + >>> is_palindrome_dict(ListNode(1, ListNode(2))) + False + + >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(1)))) + True + + >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) + True + """ if not head or not head.next: return True d = {} @@ -75,3 +150,8 @@ def is_palindrome_dict(head): if middle > 1: return False return True + +if __name__ == "__main__": + import doctest + + doctest.testmod() \ No newline at end of file From de9280ce2f7de7824d592f2877d1b230990714a4 Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 13:13:44 +0530 Subject: [PATCH 02/11] test complete --- data_structures/linked_list/is_palindrome.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index dc69948d0a52..1974c69af37d 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,3 +1,8 @@ +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + def is_palindrome(head): """ Check if a linked list is a palindrome. From 1e9a24cec8addff93a6616da35b7d891e83662dd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Oct 2023 07:48:28 +0000 Subject: [PATCH 03/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/linked_list/is_palindrome.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index dc69948d0a52..5c98c3aa7ddd 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -151,7 +151,8 @@ def is_palindrome_dict(head): return False return True + if __name__ == "__main__": import doctest - doctest.testmod() \ No newline at end of file + doctest.testmod() From 8e984f102f18dd926b6ef824749a63dac67e65f6 Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 13:19:20 +0530 Subject: [PATCH 04/11] format --- data_structures/linked_list/is_palindrome.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index 1974c69af37d..6542e67af98b 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -3,6 +3,7 @@ def __init__(self, val=0, next=None): self.val = val self.next = next + def is_palindrome(head): """ Check if a linked list is a palindrome. @@ -156,7 +157,8 @@ def is_palindrome_dict(head): return False return True + if __name__ == "__main__": import doctest - doctest.testmod() \ No newline at end of file + doctest.testmod() From e92c9e60f75cc5824aff2768d0d27bc745aac9a7 Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 13:22:34 +0530 Subject: [PATCH 05/11] ruff update --- data_structures/linked_list/is_palindrome.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index 6542e67af98b..548ddddb21ce 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,7 +1,7 @@ class ListNode: - def __init__(self, val=0, next=None): + def __init__(self, val=0, next_node=None): self.val = val - self.next = next + self.next = next_node def is_palindrome(head): From dd72ef897b54411210e4a3d066b8ab4e6cf185ab Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 13:45:43 +0530 Subject: [PATCH 06/11] cover line 154 --- data_structures/linked_list/is_palindrome.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index 548ddddb21ce..6b26f50dd0ff 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -130,6 +130,11 @@ def is_palindrome_dict(head): >>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) True + + >>> is_palindrome_dict(\ + ListNode(\ + 1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1))))))) + False """ if not head or not head.next: return True From 3808ddcf3cc0fd9b59ab82848d694b8179d2df31 Mon Sep 17 00:00:00 2001 From: Sai Harsha Kottapalli Date: Sun, 8 Oct 2023 14:07:12 +0530 Subject: [PATCH 07/11] Update data_structures/linked_list/is_palindrome.py Co-authored-by: Christian Clauss --- data_structures/linked_list/is_palindrome.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index 6b26f50dd0ff..cbe51f196c61 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -4,12 +4,12 @@ def __init__(self, val=0, next_node=None): self.next = next_node -def is_palindrome(head): +def is_palindrome(head: Node) -> bool: """ Check if a linked list is a palindrome. Args: - head (ListNode): The head of the linked list. + head: The head of the linked list. Returns: bool: True if the linked list is a palindrome, False otherwise. From 6b5629a99945d249aa77677cf050ab54aafac124 Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 14:17:46 +0530 Subject: [PATCH 08/11] use dataclass --- data_structures/linked_list/is_palindrome.py | 51 +++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index cbe51f196c61..ab69c39b27ce 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,10 +1,13 @@ +from dataclasses import dataclass + + +@dataclass class ListNode: - def __init__(self, val=0, next_node=None): - self.val = val - self.next = next_node + val: int = 0 + next_node: "ListNode" = None -def is_palindrome(head: Node) -> bool: +def is_palindrome(head: ListNode) -> bool: """ Check if a linked list is a palindrome. @@ -33,17 +36,17 @@ def is_palindrome(head: Node) -> bool: if not head: return True # split the list to two parts - fast, slow = head.next, head - while fast and fast.next: - fast = fast.next.next - slow = slow.next - second = slow.next - slow.next = None # Don't forget here! But forget still works! + fast, slow = head.next_node, head + while fast and fast.next_node: + fast = fast.next_node.next_node + slow = slow.next_node + second = slow.next_node + slow.next_node = None # Don't forget here! But forget still works! # reverse the second part node = None while second: - nxt = second.next - second.next = node + nxt = second.next_node + second.next_node = node node = second second = nxt # compare two parts @@ -51,12 +54,12 @@ def is_palindrome(head: Node) -> bool: while node: if node.val != head.val: return False - node = node.next - head = head.next + node = node.next_node + head = head.next_node return True -def is_palindrome_stack(head): +def is_palindrome_stack(head: ListNode) -> bool: """ Check if a linked list is a palindrome using a stack. @@ -82,30 +85,30 @@ def is_palindrome_stack(head): >>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(2, ListNode(1))))) True """ - if not head or not head.next: + if not head or not head.next_node: return True # 1. Get the midpoint (slow) slow = fast = cur = head - while fast and fast.next: - fast, slow = fast.next.next, slow.next + while fast and fast.next_node: + fast, slow = fast.next_node.next_node, slow.next_node # 2. Push the second half into the stack stack = [slow.val] - while slow.next: - slow = slow.next + while slow.next_node: + slow = slow.next_node stack.append(slow.val) # 3. Comparison while stack: if stack.pop() != cur.val: return False - cur = cur.next + cur = cur.next_node return True -def is_palindrome_dict(head): +def is_palindrome_dict(head: ListNode) -> bool: """ Check if a linked list is a palindrome using a dictionary. @@ -136,7 +139,7 @@ def is_palindrome_dict(head): 1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1))))))) False """ - if not head or not head.next: + if not head or not head.next_node: return True d = {} pos = 0 @@ -145,7 +148,7 @@ def is_palindrome_dict(head): d[head.val].append(pos) else: d[head.val] = [pos] - head = head.next + head = head.next_node pos += 1 checksum = pos - 1 middle = 0 From 2299c0fa1204c9a62f620f761ab7271351919900 Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 14:21:39 +0530 Subject: [PATCH 09/11] pre-commit fix --- data_structures/linked_list/is_palindrome.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index ab69c39b27ce..248f30253ccc 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,10 +1,11 @@ from dataclasses import dataclass +from typing import Optional @dataclass class ListNode: val: int = 0 - next_node: "ListNode" = None + next_node: Optional["ListNode"] = None def is_palindrome(head: ListNode) -> bool: From 320617417a4de6a11899d70343617dfab553df51 Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 22:47:45 +0530 Subject: [PATCH 10/11] Fix mypy errors --- data_structures/linked_list/is_palindrome.py | 58 ++++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index 248f30253ccc..c49d26ca0101 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,14 +1,13 @@ from dataclasses import dataclass -from typing import Optional @dataclass class ListNode: val: int = 0 - next_node: Optional["ListNode"] = None + next_node: "ListNode | None" = None -def is_palindrome(head: ListNode) -> bool: +def is_palindrome(head: "ListNode | None") -> bool: """ Check if a linked list is a palindrome. @@ -37,14 +36,18 @@ def is_palindrome(head: ListNode) -> bool: if not head: return True # split the list to two parts - fast, slow = head.next_node, head + fast: "ListNode | None" = head.next_node + slow: "ListNode | None" = head while fast and fast.next_node: fast = fast.next_node.next_node - slow = slow.next_node - second = slow.next_node - slow.next_node = None # Don't forget here! But forget still works! + slow = slow.next_node if slow else None + if slow: + # slow will always be defined, + # adding this check to resolve mypy static check + second = slow.next_node + slow.next_node = None # Don't forget here! But forget still works! # reverse the second part - node = None + node: "ListNode | None" = None while second: nxt = second.next_node second.next_node = node @@ -52,7 +55,7 @@ def is_palindrome(head: ListNode) -> bool: second = nxt # compare two parts # second part has the same or one less node - while node: + while node and head: if node.val != head.val: return False node = node.next_node @@ -60,7 +63,7 @@ def is_palindrome(head: ListNode) -> bool: return True -def is_palindrome_stack(head: ListNode) -> bool: +def is_palindrome_stack(head: "ListNode | None") -> bool: """ Check if a linked list is a palindrome using a stack. @@ -90,26 +93,33 @@ def is_palindrome_stack(head: ListNode) -> bool: return True # 1. Get the midpoint (slow) - slow = fast = cur = head + slow: "ListNode | None" = head + fast: "ListNode | None" = head while fast and fast.next_node: - fast, slow = fast.next_node.next_node, slow.next_node + fast = fast.next_node.next_node + slow = slow.next_node if slow else None - # 2. Push the second half into the stack - stack = [slow.val] - while slow.next_node: - slow = slow.next_node - stack.append(slow.val) + # slow will always be defined, + # adding this check to resolve mypy static check + if slow: + stack = [slow.val] - # 3. Comparison - while stack: - if stack.pop() != cur.val: - return False - cur = cur.next_node + # 2. Push the second half into the stack + while slow.next_node: + slow = slow.next_node + stack.append(slow.val) + + # 3. Comparison + cur: "ListNode | None" = head + while stack and cur: + if stack.pop() != cur.val: + return False + cur = cur.next_node return True -def is_palindrome_dict(head: ListNode) -> bool: +def is_palindrome_dict(head: "ListNode | None") -> bool: """ Check if a linked list is a palindrome using a dictionary. @@ -142,7 +152,7 @@ def is_palindrome_dict(head: ListNode) -> bool: """ if not head or not head.next_node: return True - d = {} + d: dict[int, list[int]] = {} pos = 0 while head: if head.val in d: From 331ca32206da5a1deac20bc2161aed260917c3aa Mon Sep 17 00:00:00 2001 From: Harsha Kottapalli Date: Sun, 8 Oct 2023 23:24:22 +0530 Subject: [PATCH 11/11] use future annotations --- data_structures/linked_list/is_palindrome.py | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/data_structures/linked_list/is_palindrome.py b/data_structures/linked_list/is_palindrome.py index c49d26ca0101..7d89f085c67f 100644 --- a/data_structures/linked_list/is_palindrome.py +++ b/data_structures/linked_list/is_palindrome.py @@ -1,13 +1,15 @@ +from __future__ import annotations + from dataclasses import dataclass @dataclass class ListNode: val: int = 0 - next_node: "ListNode | None" = None + next_node: ListNode | None = None -def is_palindrome(head: "ListNode | None") -> bool: +def is_palindrome(head: ListNode | None) -> bool: """ Check if a linked list is a palindrome. @@ -36,8 +38,8 @@ def is_palindrome(head: "ListNode | None") -> bool: if not head: return True # split the list to two parts - fast: "ListNode | None" = head.next_node - slow: "ListNode | None" = head + fast: ListNode | None = head.next_node + slow: ListNode | None = head while fast and fast.next_node: fast = fast.next_node.next_node slow = slow.next_node if slow else None @@ -47,7 +49,7 @@ def is_palindrome(head: "ListNode | None") -> bool: second = slow.next_node slow.next_node = None # Don't forget here! But forget still works! # reverse the second part - node: "ListNode | None" = None + node: ListNode | None = None while second: nxt = second.next_node second.next_node = node @@ -63,7 +65,7 @@ def is_palindrome(head: "ListNode | None") -> bool: return True -def is_palindrome_stack(head: "ListNode | None") -> bool: +def is_palindrome_stack(head: ListNode | None) -> bool: """ Check if a linked list is a palindrome using a stack. @@ -93,8 +95,8 @@ def is_palindrome_stack(head: "ListNode | None") -> bool: return True # 1. Get the midpoint (slow) - slow: "ListNode | None" = head - fast: "ListNode | None" = head + slow: ListNode | None = head + fast: ListNode | None = head while fast and fast.next_node: fast = fast.next_node.next_node slow = slow.next_node if slow else None @@ -110,7 +112,7 @@ def is_palindrome_stack(head: "ListNode | None") -> bool: stack.append(slow.val) # 3. Comparison - cur: "ListNode | None" = head + cur: ListNode | None = head while stack and cur: if stack.pop() != cur.val: return False @@ -119,7 +121,7 @@ def is_palindrome_stack(head: "ListNode | None") -> bool: return True -def is_palindrome_dict(head: "ListNode | None") -> bool: +def is_palindrome_dict(head: ListNode | None) -> bool: """ Check if a linked list is a palindrome using a dictionary.