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