今天是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 -掃碼關注,獲取更多優質文章