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%很開心!