1. 程式人生 > 其它 >打敗演算法 —— 刪除連結串列的倒數第n個結點

打敗演算法 —— 刪除連結串列的倒數第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