-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Expand file tree
/
Copy pathis_palindrome.py
More file actions
124 lines (100 loc) · 2.99 KB
/
is_palindrome.py
File metadata and controls
124 lines (100 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""
Palindrome Linked List
Determine whether a singly linked list is a palindrome. Three approaches are
provided: reverse-half, stack-based, and dictionary-based.
Reference: https://leetcode.com/problems/palindrome-linked-list/
Complexity (reverse-half):
Time: O(n)
Space: O(1)
"""
from __future__ import annotations
def is_palindrome(head: object | None) -> bool:
"""Check if a linked list is a palindrome by reversing the second half.
Args:
head: Head node of the linked list (must have .val and .next attrs).
Returns:
True if the list is a palindrome, False otherwise.
Examples:
>>> is_palindrome(None)
True
"""
if not head:
return True
fast, slow = head.next, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
second = slow.next
slow.next = None
node = None
while second:
nxt = second.next
second.next = node
node = second
second = nxt
while node:
if node.val != head.val:
return False
node = node.next
head = head.next
return True
def is_palindrome_stack(head: object | None) -> bool:
"""Check if a linked list is a palindrome using a stack.
Args:
head: Head node of the linked list.
Returns:
True if the list is a palindrome, False otherwise.
Examples:
>>> is_palindrome_stack(None)
True
"""
if not head or not head.next:
return True
slow = fast = current = head
while fast and fast.next:
fast, slow = fast.next.next, slow.next
stack = [slow.val]
while slow.next:
slow = slow.next
stack.append(slow.val)
while stack:
if stack.pop() != current.val:
return False
current = current.next
return True
def is_palindrome_dict(head: object | None) -> bool:
"""Check if a linked list is a palindrome using a dictionary of positions.
Builds a dictionary mapping each value to its list of positions, then
verifies that positions are symmetric around the center.
Args:
head: Head node of the linked list.
Returns:
True if the list is a palindrome, False otherwise.
Examples:
>>> is_palindrome_dict(None)
True
"""
if not head or not head.next:
return True
positions: dict[object, list[int]] = {}
pos = 0
current = head
while current:
if current.val in positions:
positions[current.val].append(pos)
else:
positions[current.val] = [pos]
current = current.next
pos += 1
checksum = pos - 1
middle = 0
for indices in positions.values():
if len(indices) % 2 != 0:
middle += 1
else:
for step, i in enumerate(range(len(indices))):
if indices[i] + indices[len(indices) - 1 - step] != checksum:
return False
if middle > 1:
return False
return True