程式設計師面試金典 - 面試題 02.06. 迴文連結串列
阿新 • • 發佈:2020-12-19
技術標籤:Leetcode連結串列leetcode資料結構演算法
題目難度: 簡單
今天繼續更新程式設計師面試金典系列, 大家在公眾號 演算法精選 裡回覆 面試金典 就能看到該系列當前連載的所有文章了, 記得關注哦~
題目描述
編寫一個函式,檢查輸入的連結串列是否是迴文的。
示例 1:
- 輸入: 1->2
- 輸出: false
示例 2:
- 輸入: 1->2->2->1
- 輸出: true
題目思考
- 你能否用 O(n) 時間複雜度和 O(1) 空間複雜度解決此題?
解決方案
思路
- 相比傳統的陣列或者字串求迴文, 連結串列不能直接取下標, 也無法像雙向連結串列那樣用雙指標往中間靠攏比較的方法, 如何做到 O(n) 時間複雜度和 O(1) 空間複雜度呢?
- 分析雙指標往中間靠攏比較的原理, 它是第一個和最後一個比較, 第二個和倒數第二個比較, 以此類推…
- 所以我們可以人為將連結串列分成左右兩半部分, 然後將右半部分進行反轉, 再用雙指標從兩部分的頭節點依次往後比較即可
- 這樣就把問題分解成了 3 個經典的小問題, 它們都可以利用雙指標來解決, 大家可以結合下面的程式碼和註釋進行理解
- 如果題目要求恢復原來連結串列結構的話, 還可以將連結串列翻轉部分提取出來, 然後比較完之後再將右半部分轉回去, 下面程式碼也實現了這一步
複雜度
- 時間複雜度 O(N): 只需要遍歷每個節點三遍(劃分/翻轉/比較)
- 空間複雜度 O(1): 只使用了幾個常數空間的變數
程式碼
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
# 把右半部分翻轉後再雙指標依次比較, 最後將輸入連結串列結構恢復成原樣
# 第一步: 使用快慢指標找中點
fast = head
slow = head
while fast:
fast = fast.next
if fast:
fast = fast.next
slow = slow. next
# 此時slow就是右半部分的頭節點(左半部分長度可能相等或多1, 對應總長度是偶數和奇數的情況)
# 第二步: 定義翻轉函式, 將右半部分進行翻轉
def reverse(cur):
# 翻轉並返回新的頭
pre = None
while cur:
nex = cur.next
cur.next = pre
pre, cur = cur, nex
return pre
# 第三步: 雙指標依次比較左右兩部分對應節點, 並記錄右半部分末尾節點用於恢復原始結構
left, right = head, reverse(slow)
rightTail = right
res = True
while left and right:
if left.val != right.val:
res = False
break
left = left.next
rightTail = right
right = right.next
# 得到右半部分末尾節點並翻轉恢復(注意它可能不存在, 如果當原始連結串列長度小於等於1時)
while rightTail and rightTail.next:
rightTail = rightTail.next
reverse(rightTail)
return res
大家可以在下面這些地方找到我~