Add DocTests to is_palindrome.py (#10081)

* add doctest ut

* test complete

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* format

* ruff update

* cover line 154

* Update data_structures/linked_list/is_palindrome.py

Co-authored-by: Christian Clauss <cclauss@me.com>

* use dataclass

* pre-commit fix

* Fix mypy errors

* use future annotations

---------

Co-authored-by: Harsha Kottapalli <skottapalli@microsoft.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
Sai Harsha Kottapalli 2023-10-09 17:36:16 +05:30 committed by GitHub
parent ed19b1cf0c
commit 12e8e9ca87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,65 +1,167 @@
def is_palindrome(head): from __future__ import annotations
from dataclasses import dataclass
@dataclass
class ListNode:
val: int = 0
next_node: ListNode | None = None
def is_palindrome(head: ListNode | None) -> bool:
"""
Check if a linked list is a palindrome.
Args:
head: 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: if not head:
return True return True
# split the list to two parts # split the list to two parts
fast, slow = head.next, head fast: ListNode | None = head.next_node
while fast and fast.next: slow: ListNode | None = head
fast = fast.next.next while fast and fast.next_node:
slow = slow.next fast = fast.next_node.next_node
second = slow.next slow = slow.next_node if slow else None
slow.next = None # Don't forget here! But forget still works! 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 # reverse the second part
node = None node: ListNode | None = None
while second: while second:
nxt = second.next nxt = second.next_node
second.next = node second.next_node = node
node = second node = second
second = nxt second = nxt
# compare two parts # compare two parts
# second part has the same or one less node # second part has the same or one less node
while node: while node and head:
if node.val != head.val: if node.val != head.val:
return False return False
node = node.next node = node.next_node
head = head.next head = head.next_node
return True return True
def is_palindrome_stack(head): def is_palindrome_stack(head: ListNode | None) -> bool:
if not head or not head.next: """
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_node:
return True return True
# 1. Get the midpoint (slow) # 1. Get the midpoint (slow)
slow = fast = cur = head slow: ListNode | None = head
while fast and fast.next: fast: ListNode | None = head
fast, slow = fast.next.next, slow.next while fast and fast.next_node:
fast = fast.next_node.next_node
slow = slow.next_node if slow else None
# slow will always be defined,
# adding this check to resolve mypy static check
if slow:
stack = [slow.val]
# 2. Push the second half into the stack # 2. Push the second half into the stack
stack = [slow.val] while slow.next_node:
while slow.next: slow = slow.next_node
slow = slow.next
stack.append(slow.val) stack.append(slow.val)
# 3. Comparison # 3. Comparison
while stack: cur: ListNode | None = head
while stack and cur:
if stack.pop() != cur.val: if stack.pop() != cur.val:
return False return False
cur = cur.next cur = cur.next_node
return True return True
def is_palindrome_dict(head): def is_palindrome_dict(head: ListNode | None) -> bool:
if not head or not head.next: """
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
>>> is_palindrome_dict(\
ListNode(\
1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1)))))))
False
"""
if not head or not head.next_node:
return True return True
d = {} d: dict[int, list[int]] = {}
pos = 0 pos = 0
while head: while head:
if head.val in d: if head.val in d:
d[head.val].append(pos) d[head.val].append(pos)
else: else:
d[head.val] = [pos] d[head.val] = [pos]
head = head.next head = head.next_node
pos += 1 pos += 1
checksum = pos - 1 checksum = pos - 1
middle = 0 middle = 0
@ -75,3 +177,9 @@ def is_palindrome_dict(head):
if middle > 1: if middle > 1:
return False return False
return True return True
if __name__ == "__main__":
import doctest
doctest.testmod()