打敗演算法 —— 刪除連結串列的倒數第n個結點
本文參考
出自LeetCode上的題庫 —— 刪除連結串列的倒數第n個結點,官方的雙指標解法沒有完全符合"只遍歷一遍連結串列"的要求,本文給出另一種雙指標解法
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
移動零問題
給你一個連結串列,刪除連結串列的倒數第 n 個結點,並且返回連結串列的頭結點
示例1:
輸入:head = [1,2,3,4,5], n = 2
輸出:[1,2,3,5]
示例 2:
輸入:head = [1], n = 1
輸出:[]
示例 3:
輸入:head = [1,2], n = 1
輸出:[1]
解題思路
第一種解法,先遍歷一遍原列表,記錄連結串列長度,然後再遍歷一遍就能夠刪除對應的結點
第二種解法利用雙指標,我們"迴文連結串列"(https://www.cnblogs.com/kuluo/p/15955593.html)中通過雙指標找到中間節點(slow指標所在的位置),因此也能夠計算連結串列的長度。獲取到連結串列的長度後,根據倒數第n個結點是在slow指標前還是在slow指標後,決定是從head頭節點開始找,還是從slow指標所指的結點開始找,相當於減少了一半的結點遍歷過程
在這道題中,我們看到雙指標不但能夠使演算法保持在$O(n)$的時間複雜度,也能將空間複雜度限制在常數級$O(1)$
雙指標解法
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next: ListNode = next
class Solution:
def remove_Nth_from_end(self, head: ListNode, n: int) -> ListNode:
cnt = 1
slow = fast = head
#通過快慢指標獲得連結串列長度
while fast.next and fast.next.next:
cnt += 1
slow = slow.next
fast = fast.next.next
#計算連結串列長度
if fast.next is None:
length = cnt * 2 - 1
else:
length = cnt * 2
#特殊情況
if n == length:
return head.next
#連結串列的前半部分
# length = 5, n = 2, step = 5 - 2 - 3 + 1 = 1
if n > length / 2:
step = length - n
curr = pre = head
#連結串列的後半部分
else:
step = length - n - cnt + 1
curr = pre = slow
#獲得步長後移動到特定位置並刪除指標
for i in range(step):
pre = curr
curr = curr.next
curr = curr.next
pre.next = curr
return head