Description
給定一個單向鏈結串列的頭節點
head,判斷該鏈結串列是否為回文(palindrome)。
Example:
Input: head = [1,2,2,1] Output: true
Intuition#
核心思路:找到中點後反轉後半段,再逐一比對前半段與反轉後的後半段。
- 回文的特性是前後對稱,但單向鏈結串列只能往前走
- 方法一:用 Stack 或陣列存所有值,再雙向比對
- 方法二:快慢指針找中點 + 反轉後半段,O(1) 空間
Approaches#
1: Stack#
- 概念: 將所有節點值壓入 Stack,再從頭遍歷同時從 Stack 彈出比對
- 時間複雜度:
O(n) - 空間複雜度:
O(n)- Stack 儲存所有節點值
class Solution {
fun isPalindrome(head: ListNode?): Boolean {
val stack = ArrayDeque<Int>()
var curr = head
while (curr != null) {
stack.addLast(curr.`val`)
curr = curr.next
}
curr = head
while (curr != null) {
if (curr.`val` != stack.removeLast()) return false
curr = curr.next
}
return true
}
}⭐2: Fast-Slow Pointer + Reverse#
- 概念: 快慢指針找中點,反轉後半段鏈結串列,再逐一比對
- 時間複雜度:
O(n) - 空間複雜度:
O(1)- 只用指針變數
class Solution {
fun isPalindrome(head: ListNode?): Boolean {
// 快慢指針找中點
var slow = head
var fast = head
while (fast?.next != null) {
slow = slow?.next
fast = fast.next?.next
}
// 反轉後半段
var prev: ListNode? = null
var curr = slow
while (curr != null) {
val next = curr.next
curr.next = prev
prev = curr
curr = next
}
// 比對前半段與反轉後的後半段
var left = head
var right = prev
while (right != null) {
if (left?.`val` != right.`val`) return false
left = left.next
right = right.next
}
return true
}
}面試時可能會被問到是否需要還原鏈結串列。若需要,在比對完成後再反轉一次後半段即可。
🔑 Takeaways#
- Pattern: 快慢指針找中點 + 鏈結串列反轉,是鏈結串列回文判斷的經典組合
- 關鍵技巧: 快慢指針停下時,slow 剛好在中間(奇數長度)或後半段起點(偶數長度),可以直接從 slow 開始反轉