今天是LeetCode專題的第58篇文章,我們一起來看看LeetCode 92題,翻轉連結串列II(Reverse LInked List II)。
這題的官方難度是Medium,2451個贊同,145個反對,通過率38.6%。從這份資料上我們也看得出來,這題的質量很高,廣受好評。也的確如此,這是一道非常經典的連結串列問題,不僅考驗我們對於連結串列的理解和掌握,而且對基本功的要求也很高。
題意
給定一個連結串列和兩個整數m和n,m和n分別代表連結串列當中的第m和第n個元素,其中m <= n。要求我們通過一次遍歷將連結串列當中m到n這一段元素進行翻轉。
樣例
Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL
題解
這題的題意很直接,就是讓我們翻轉連結串列當中指定的部分,並且只能通過一次遍歷完成。
我們很容易想明白,通過m和n我們可以將連結串列分成三個部分:
分別是m左側的,m到n之間的也就是我們需要翻轉的部分,以及n右側的部分。當然這裡可能存在一個特殊情況,m左側和n的右側都有可能沒有元素,我們可以先忽略,先考慮最一般的情況。
翻轉連結串列我們最常用的方法就是先把[m, n]區間裡的元素先儲存起來,人工翻轉了之後,再重新構建成一段連結串列,替換原連結串列對應的部分。但是很明顯也可以發現,這樣做是不符合題目要求的。因為我們儲存元素遍歷了連結串列一次,我們在構建連結串列的時候又遍歷了一次,至少需要兩次遍歷才可以完成。
那怎麼樣才能一次遍歷完成連結串列的翻轉呢?其實也很簡單,我們只需要倒敘插入就好了。
比如我們有這樣一段連結串列,我們想要翻轉其中2、3、4這三個節點:
首先,我們從1開始,1是翻轉之後的起始部分。我們先記錄下1的位置,這裡1指向2保持不變。對於2號節點我們需要記錄下它的後繼,這裡我們用tmp記錄2號節點的後繼,也就是3號節點。之後我們將2號節點的後繼置為None。
再下一步,我們用tmp記錄3號節點的後繼,將3號節點的後繼指向2號節點。
最後,我們如法炮製,將4號節點的後繼指向3號節點,將1號節點的後繼指向4號節點,並且將2號節點的後繼指向4號節點的原後繼,也就是None。這樣我們就完成了連結串列部分的翻轉,其中的原理很簡單,就是利用了連結串列遍歷和插入時候的性質,每次將需要翻轉部分元素的後繼指向它們原本的前驅。這句話有些拗口,但是多讀幾遍也就理解了。
這個思路非常簡單,但是用程式碼實現卻不容易,很容易寫錯。尤其是在賦值的時候,很容易搞錯指標到底應該指向哪裡,到底需要哪些臨時變數。關於這些內容沒有太好的辦法,只能加強訓練,提升自己的基本功。
最後, 我們考慮一下特殊情況。題目當中的特殊情況有兩種, 第一種是m=1,第二種是n=連結串列末尾。其中第二種不算是特殊情況,因為在連結串列當中末尾的元素也有後繼,就是None。但是m=1的情況需要小心,因為m=1的時候,翻轉部分是沒有前驅的,稍稍有些不同。解決也很簡單,我們單獨特判一下這種情況,人為建立一個前驅節點即可。
貼上程式碼:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
if m == n:
return head
pnt = head
# 移動m-2格,到達翻轉部分的前驅節點
for i in range(m-2):
pnt = pnt.next
flag = False
# 特判m=1的情況,如果m=1那麼人為製造一個節點作為前驅
if m == 1:
flag = True
pnt = ListNode(0)
pnt.next = head
head = pnt
# cur即當前待翻轉的節點
cur = pnt.next
# pre表示cur的前驅
pre = pnt
# last表示最後一個翻轉的元素
last = pnt.next
for i in range(m, n+1):
# 先記錄下當前節點的後繼
nxt = cur.next
# 將當前節點的後繼指向前驅
cur.next = pre
pnt.next = cur
pre = cur
cur = nxt
# 將last指向翻轉之後的元素,將連結串列串起來
last.next = cur
return head.next if flag else head
總結
連結串列的相關操作非常考驗基本功,雖然演算法簡單,但是對連結串列不熟悉,邏輯思考能力稍稍弱一些的同學想要做出這道題還是比較困難的。也因此,很多公司在面試的時候喜歡詢問連結串列相關的操作和演算法,以此考察候選人的基本功。如果想要進入大公司的,建議好好練習一下相關的問題,一定會有幫助的。
今天的文章到這裡就結束了,如果喜歡本文的話,請來一波素質三連,給我一點支援吧(關注、轉發、點贊)。
- END -掃碼關注,獲取更多優質文章