Skip to content

Commit 12e8e9c

Browse files
SaiHarshaKHarsha Kottapallipre-commit-ci[bot]cclauss
authored
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 <[email protected]> * use dataclass * pre-commit fix * Fix mypy errors * use future annotations --------- Co-authored-by: Harsha Kottapalli <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss <[email protected]>
1 parent ed19b1c commit 12e8e9c

File tree

1 file changed

+142
-34
lines changed

1 file changed

+142
-34
lines changed
+142-34
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,167 @@
1-
def is_palindrome(head):
1+
from __future__ import annotations
2+
3+
from dataclasses import dataclass
4+
5+
6+
@dataclass
7+
class ListNode:
8+
val: int = 0
9+
next_node: ListNode | None = None
10+
11+
12+
def is_palindrome(head: ListNode | None) -> bool:
13+
"""
14+
Check if a linked list is a palindrome.
15+
16+
Args:
17+
head: The head of the linked list.
18+
19+
Returns:
20+
bool: True if the linked list is a palindrome, False otherwise.
21+
22+
Examples:
23+
>>> is_palindrome(None)
24+
True
25+
26+
>>> is_palindrome(ListNode(1))
27+
True
28+
29+
>>> is_palindrome(ListNode(1, ListNode(2)))
30+
False
31+
32+
>>> is_palindrome(ListNode(1, ListNode(2, ListNode(1))))
33+
True
34+
35+
>>> is_palindrome(ListNode(1, ListNode(2, ListNode(2, ListNode(1)))))
36+
True
37+
"""
238
if not head:
339
return True
440
# split the list to two parts
5-
fast, slow = head.next, head
6-
while fast and fast.next:
7-
fast = fast.next.next
8-
slow = slow.next
9-
second = slow.next
10-
slow.next = None # Don't forget here! But forget still works!
41+
fast: ListNode | None = head.next_node
42+
slow: ListNode | None = head
43+
while fast and fast.next_node:
44+
fast = fast.next_node.next_node
45+
slow = slow.next_node if slow else None
46+
if slow:
47+
# slow will always be defined,
48+
# adding this check to resolve mypy static check
49+
second = slow.next_node
50+
slow.next_node = None # Don't forget here! But forget still works!
1151
# reverse the second part
12-
node = None
52+
node: ListNode | None = None
1353
while second:
14-
nxt = second.next
15-
second.next = node
54+
nxt = second.next_node
55+
second.next_node = node
1656
node = second
1757
second = nxt
1858
# compare two parts
1959
# second part has the same or one less node
20-
while node:
60+
while node and head:
2161
if node.val != head.val:
2262
return False
23-
node = node.next
24-
head = head.next
63+
node = node.next_node
64+
head = head.next_node
2565
return True
2666

2767

28-
def is_palindrome_stack(head):
29-
if not head or not head.next:
68+
def is_palindrome_stack(head: ListNode | None) -> bool:
69+
"""
70+
Check if a linked list is a palindrome using a stack.
71+
72+
Args:
73+
head (ListNode): The head of the linked list.
74+
75+
Returns:
76+
bool: True if the linked list is a palindrome, False otherwise.
77+
78+
Examples:
79+
>>> is_palindrome_stack(None)
80+
True
81+
82+
>>> is_palindrome_stack(ListNode(1))
83+
True
84+
85+
>>> is_palindrome_stack(ListNode(1, ListNode(2)))
86+
False
87+
88+
>>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(1))))
89+
True
90+
91+
>>> is_palindrome_stack(ListNode(1, ListNode(2, ListNode(2, ListNode(1)))))
92+
True
93+
"""
94+
if not head or not head.next_node:
3095
return True
3196

3297
# 1. Get the midpoint (slow)
33-
slow = fast = cur = head
34-
while fast and fast.next:
35-
fast, slow = fast.next.next, slow.next
36-
37-
# 2. Push the second half into the stack
38-
stack = [slow.val]
39-
while slow.next:
40-
slow = slow.next
41-
stack.append(slow.val)
42-
43-
# 3. Comparison
44-
while stack:
45-
if stack.pop() != cur.val:
46-
return False
47-
cur = cur.next
98+
slow: ListNode | None = head
99+
fast: ListNode | None = head
100+
while fast and fast.next_node:
101+
fast = fast.next_node.next_node
102+
slow = slow.next_node if slow else None
103+
104+
# slow will always be defined,
105+
# adding this check to resolve mypy static check
106+
if slow:
107+
stack = [slow.val]
108+
109+
# 2. Push the second half into the stack
110+
while slow.next_node:
111+
slow = slow.next_node
112+
stack.append(slow.val)
113+
114+
# 3. Comparison
115+
cur: ListNode | None = head
116+
while stack and cur:
117+
if stack.pop() != cur.val:
118+
return False
119+
cur = cur.next_node
48120

49121
return True
50122

51123

52-
def is_palindrome_dict(head):
53-
if not head or not head.next:
124+
def is_palindrome_dict(head: ListNode | None) -> bool:
125+
"""
126+
Check if a linked list is a palindrome using a dictionary.
127+
128+
Args:
129+
head (ListNode): The head of the linked list.
130+
131+
Returns:
132+
bool: True if the linked list is a palindrome, False otherwise.
133+
134+
Examples:
135+
>>> is_palindrome_dict(None)
136+
True
137+
138+
>>> is_palindrome_dict(ListNode(1))
139+
True
140+
141+
>>> is_palindrome_dict(ListNode(1, ListNode(2)))
142+
False
143+
144+
>>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(1))))
145+
True
146+
147+
>>> is_palindrome_dict(ListNode(1, ListNode(2, ListNode(2, ListNode(1)))))
148+
True
149+
150+
>>> is_palindrome_dict(\
151+
ListNode(\
152+
1, ListNode(2, ListNode(1, ListNode(3, ListNode(2, ListNode(1)))))))
153+
False
154+
"""
155+
if not head or not head.next_node:
54156
return True
55-
d = {}
157+
d: dict[int, list[int]] = {}
56158
pos = 0
57159
while head:
58160
if head.val in d:
59161
d[head.val].append(pos)
60162
else:
61163
d[head.val] = [pos]
62-
head = head.next
164+
head = head.next_node
63165
pos += 1
64166
checksum = pos - 1
65167
middle = 0
@@ -75,3 +177,9 @@ def is_palindrome_dict(head):
75177
if middle > 1:
76178
return False
77179
return True
180+
181+
182+
if __name__ == "__main__":
183+
import doctest
184+
185+
doctest.testmod()

0 commit comments

Comments
 (0)