125. Valid Palindrome
Quick Identifier
- Topic: two-pointers
- Pattern: inward convergence with skipping
- Difficulty: Easy
- Companies: Amazon, Google, Meta, Microsoft, Apple
- Frequency: High
- LeetCode: 125
- Hint: Skip characters that do not matter, then compare the meaningful left and right characters.
Quick Sights
- Time Complexity:
O(n)- every character is skipped or compared at most once. - Space Complexity:
O(1)- no cleaned copy is needed. - Core Mechanism: Move
left/rightinward, skipping non-alphanumeric characters and comparing normalized values.
Concept Explanation
A palindrome proof settles pairs from the outside in. Everything outside the current window has either matched or been ignored.
Spaces and punctuation are not part of the problem, so the pointers skip them before comparing. Once a pair matches, both characters are permanently settled and the window shrinks.
Diagram
This diagram shows the pointer decisions and the step-by-step algorithm flow.
Loading diagram…
Study Pattern (SDE3+)
- 0-3 min: Name the pointer shape, then state the invariant and discard rule before coding.
- Implementation pass: Keep pointer movement explicit; every branch must return, record an answer, or move at least one pointer.
- 5 min extension: Explain how the solution changes for immutable input, streaming input, many duplicate values, or many repeated queries.
Approaches
- Filter and reverse is simple but
O(n)space. Two pointers on the original string keepO(1)space.
Optimal Solution
JavaScript
function isPalindrome(text) {
let left = 0, right = text.length - 1;
while (left < right) {
while (left < right && !/[a-zA-Z0-9]/.test(text[left])) left++;
while (left < right && !/[a-zA-Z0-9]/.test(text[right])) right--;
if (text[left].toLowerCase() !== text[right].toLowerCase()) return false;
left++; right--;
}
return true;
}Go
func isPalindrome(s string) bool {
left, right := 0, len(s)-1
for left < right {
for left < right && !isAlnum(s[left]) { left++ }
for left < right && !isAlnum(s[right]) { right-- }
if toLower(s[left]) != toLower(s[right]) { return false }
left++
right--
}
return true
}
func isAlnum(b byte) bool {
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}
func toLower(b byte) byte {
if b >= 'A' && b <= 'Z' { return b + 'a' - 'A' }
return b
}Walkthrough
"A man, a plan, a canal: Panama" compares only meaningful lowercase pairs and returns true.
Edge Cases
- Empty after filtering
- Single character
- Mixed case and digits.
Pitfalls
- Forgetting skip-loop bounds
- Comparing raw case
- Allocating a cleaned string when constant space is expected.
Similar Problems
Mind-Map Tags
#two-pointers #palindrome #string
Mark this page when you finish learning it.
Last updated on
Spotted something unclear or wrong on this page?