LeetCode 92 | 大公司常考的面試題,翻轉鏈表當中指定部分

今天是LeetCode專題的第58篇文章,咱們一塊兒來看看LeetCode 92題,翻轉鏈表II(Reverse LInked List II)。web

這題的官方難度是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咱們能夠將鏈表分紅三個部分:flex

分別是m左側的,m到n之間的也就是咱們須要翻轉的部分,以及n右側的部分。固然這裏可能存在一個特殊狀況,m左側和n的右側都有可能沒有元素,咱們能夠先忽略,先考慮最通常的狀況。url

翻轉鏈表咱們最經常使用的方法就是先把[m, n]區間裏的元素先存儲起來,人工翻轉了以後,再從新構建成一段鏈表,替換原鏈表對應的部分。可是很明顯也能夠發現,這樣作是不符合題目要求的。由於咱們存儲元素遍歷了鏈表一次,咱們在構建鏈表的時候又遍歷了一次,至少須要兩次遍歷才能夠完成spa

那怎麼樣才能一次遍歷完成鏈表的翻轉呢?其實也很簡單,咱們只須要倒敘插入就行了。指針

好比咱們有這樣一段鏈表,咱們想要翻轉其中二、三、4這三個節點:code

首先,咱們從1開始,1是翻轉以後的起始部分。咱們先記錄下1的位置,這裏1指向2保持不變。對於2號節點咱們須要記錄下它的後繼,這裏咱們用tmp記錄2號節點的後繼,也就是3號節點。以後咱們將2號節點的後繼置爲None。blog

再下一步,咱們用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 -

掃碼關注,獲取更多優質文章

相關文章
相關標籤/搜索