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
+ """
2
38
if not head :
3
39
return True
4
40
# 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!
11
51
# reverse the second part
12
- node = None
52
+ node : ListNode | None = None
13
53
while second :
14
- nxt = second .next
15
- second .next = node
54
+ nxt = second .next_node
55
+ second .next_node = node
16
56
node = second
17
57
second = nxt
18
58
# compare two parts
19
59
# second part has the same or one less node
20
- while node :
60
+ while node and head :
21
61
if node .val != head .val :
22
62
return False
23
- node = node .next
24
- head = head .next
63
+ node = node .next_node
64
+ head = head .next_node
25
65
return True
26
66
27
67
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 :
30
95
return True
31
96
32
97
# 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
48
120
49
121
return True
50
122
51
123
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 :
54
156
return True
55
- d = {}
157
+ d : dict [ int , list [ int ]] = {}
56
158
pos = 0
57
159
while head :
58
160
if head .val in d :
59
161
d [head .val ].append (pos )
60
162
else :
61
163
d [head .val ] = [pos ]
62
- head = head .next
164
+ head = head .next_node
63
165
pos += 1
64
166
checksum = pos - 1
65
167
middle = 0
@@ -75,3 +177,9 @@ def is_palindrome_dict(head):
75
177
if middle > 1 :
76
178
return False
77
179
return True
180
+
181
+
182
+ if __name__ == "__main__" :
183
+ import doctest
184
+
185
+ doctest .testmod ()
0 commit comments