mirror of
https://hub.njuu.cf/TheAlgorithms/Python.git
synced 2023-10-11 13:06:12 +08:00
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:
parent
ed19b1cf0c
commit
12e8e9ca87
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user