鏈表常見操作(四)修改指針與環操作

鏈表常見操作

        OK, 八個空格, 我們繼續學習鏈表常見操作. 接下來主要分爲兩個部分的內容, 第一個部分, 總結鏈表中修改指針的題目; 第二部分, 講環形鏈表! 可能第二個部分比較有乾貨.

鏈表問題的特性 - 修改指針

        鏈表的問題實際上, 就是修改指針走向的問題. 很多時候多多畫圖, 用幾個臨時變量ListNode就可以解決了. 即使想要新建一個鏈表成本也很低, 改改指針複用之前的元素就好了. 當然, 這樣講太粗了, 等下會給出幾個題目. 當然, 這也是因爲筆者偷懶, 不知道這些題目應該歸屬到那些類型裏面去. 要注意的是, 我們經常會將指針修改後, 繼續使用, 從而導致錯誤. 例如

reverse正常版本

public ListNode reverse(ListNode head) {
    ListNode prev = null;
    ListNode tmp = head;
    while(tmp != null) {
        ListNode next = tmp.next;
        tmp.next = prev; // 記得先保存, 再修改
        prev = tmp;
        tmp = next;
    }
    return prev;
}

錯誤版本

public ListNode reverse(ListNode head) {
    ListNode prev = null;
    ListNode tmp = head;
    while(tmp != null) {
        tmp.next = prev;
        prev = tmp;
        tmp = tmp.next;
    }
    return prev;
}

應對口訣: 先保存, 再修改. 一般會形成一個環.

然後, 是修改指針有關題目: 兩兩翻轉 , K個一組翻轉 .

環操作

如果沒有環操作, 那這個系列就不完整了.

判斷是否有環的存在

環形鏈表 主要通過fast-slow指針去完成.

查找環入口元素

環形鏈表 II 需要一些理論知識 - Floyd 算法 .解析如下:

首先, 假設環的長度爲C, 非環部分的長度爲F.  再設入口節點的下標是0, 相應的head節點的下標是-F, tail節點的下標是C-1.

上圖轉自leet-code

階段一 

slow=head;
fast=head;

現在開始fast-slow迭代. 當slow走到環的入口時, 迭代進行了F次; 而fast相對於slow多走了F步, 到達下標爲h的點. 對於h, 我們有結論一: h = F mod C.(這是一個重要的結論,後面會用到. ) 

由於相對速度, fast和slow在C-h次迭代之後相遇. 此時, fast位於下標爲 h+2(C-h) = 2C-h的點, 即C-h點.  

階段二 

再設置一個指針從頭開始與fast進行同步迭代, 兩者每次行進的步長爲1. 

在F次迭代之後, 第一個指針到達入口; 而fast,位於C-h+F處, 由於結論一, fast也位於入口.  兩者相等.

相關代碼如下:

public ListNode detectCycle(ListNode head) {
    ListNode slow = head;
    ListNode fast = head;
    while (fast != null && fast.next !=null) {
        slow = slow.next;
        fast = fast.next.next;
        if (fast == slow) {
            ListNode tmp = head;
            while (tmp!=fast) {
                tmp = tmp.next;
                fast = fast.next;
            }
            return tmp;
        }
    }
    return null;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章