1. 程式人生 > >LeetCode-92. Reverse Linked List II(附思考過程和debug過程)

LeetCode-92. Reverse Linked List II(附思考過程和debug過程)

https://leetcode.com/problems/reverse-linked-list-ii/題意比較簡單,要求在一輪遍歷中將第m個節點到第n個節點逆轉,其他節點保持不變,核心程式碼與https://leetcode.com/problems/reverse-linked-list/有所不同。關鍵是從逆轉操作中抽象出“迴圈不變式”。請看如下詳解:

如下圖是一個要逆轉第3(m)到第5(n)節點,Rear指向第m-1個節點永遠不變(插入操作永遠需要一個前置節點),q與Rear的相對位置不變,始終指向待插位置的後置節點,p指向待插入節點,Front永遠指向連結串列初始狀態的第三個節點(值為3),但在後續操作中在整個連結串列中的絕對位置在變化,隨著插入操作的進行,Front和p都在後移。

                                  

下面執行p的鏈斷裂和插入操作:

                                  

                                  

                                  

迴圈不變式子程式碼如下(注意操作的先後順序):

            front.next=p.next;
            rear.next=p;
            p.next=q;
            q=p;
            p=front.next;

再看下一個重複的“鏈斷裂插入”操作結果:

                                         

全部程式碼如下:

public ListNode reverseBetween(ListNode head, int m, int n) {
        
        
        if(head==null || head.next==null)
            return head;
        ListNode virtual=new ListNode(0);
        virtual.next=head;
        
        int k=m;
        ListNode rear=virtual,front=rear.next;
        while(k>1)
        {
            rear=rear.next;
            front=front.next;
            k--;
        }

        k=n-m;
        if(k==0)
            return virtual.next;
        ListNode p=front.next,q=front;
        if(p.next==null)
        {
            rear.next=p;
            p.next=front;
            front.next=null;
            return virtual.next;
        }
        while(k>0)
        {
            front.next=p.next;
            rear.next=p;
            p.next=q;
            q=p;
            p=front.next;
            k--;
        }
        return virtual.next;
    }

當然這裡面有三處細節在任何有關於連結串列的題目中都需要注意,因為博主就是犯了其中兩個導致花了半小時debug的:

1.程式碼中設定起點指標q=p.next,都需要判斷q是否為空的情況,本題中

if(head==null || head.next==null)
            return head;
ListNode p=front.next,q=front;
if(p.next==null)
{
    rear.next=p;
    p.next=front;
    front.next=null;
    return virtual.next;
}

都不能忽略,空節點沒有next屬性。

2.當插入位置為連結串列頭時的插入程式碼跟迴圈不變式不一樣

為了解決這個問題,可以採用虛擬頭節點的方法,這樣的話插入位置永遠在連結串列中間,而不是表頭:

        ListNode virtual=new ListNode(0);
        virtual.next=head;
        
        int k=m;
        ListNode rear=virtual,front=rear.next;
        while(k>1)
        {
            rear=rear.next;
            front=front.next;
            k--;
        }

        k=n-m;
        if(k==0)
            return virtual.next;

這時的返回就是那個虛擬頭結點的下一個節點:

3.迴圈控制條件是大於0大於1還是大於2要特別思考

不過AC後的Beat 100%很開心!