鏈表中的快慢指針法


快慢指針法:

快慢指針一般都初始化指向鏈表的頭結點 head,前進時快指針 fast 在前,慢指針 slow 在后,巧妙解決一些鏈表中的問題。

1.判定鏈表中是否含有環(leetcode141.環形鏈表)

這應該屬於鏈表最基本的操作了,單鏈表的特點是每個節點只知道下一個節點,所以一個指針的話無法判斷鏈表中是否含有環的。
如果鏈表中不含環,那么這個指針最終會遇到空指針 null 表示鏈表到頭了
但是如果鏈表中含有環,那么這個指針就會陷入死循環,因為環形數組中沒有 null 指針作為尾部節點。
經典解法就是用兩個指針,一個每次前進兩步,一個每次前進一步。如果不含有環,跑得快的那個指針最終會遇到 null,說明鏈表不含環;如果含有環,快指針最終會超慢指針一圈,和慢指針相遇,說明鏈表含有環。

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast,slow;
        fast=slow=head;

        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){
                return true;
            }
        }
        return false;       
    }
}
# 這里寫法要注意的是因為fast快,所有如果無環,那么肯定是fast先碰到null
# 另外因為fast=fast.next.next; 所以fast本身可能是null,fast.next也可能是null

2.已知鏈表中含有環,返回這個環的起始位置

當快慢指針相遇時,讓其中任一個指針重新指向頭節點,然后讓它倆以相同速度前進,再次相遇時所在的節點位置就是環開始的位置。
第一次相遇時,假設慢指針 slow 走了 k 步,那么快指針 fast 一定走了 2k 步,也就是說比 slow 多走了 k 步(也就是環的長度)
設相遇點距環的起點的距離為 m,那么環的起點距頭結點 head 的距離為 k - m,也就是說如果從 head 前進 k - m 步就能到達環起點,巧的是,如果從相遇點繼續前進 k - m 步,也恰好到達環起點。
所以,只要我們把快慢指針中的任一個重新指向 head,然后兩個指針同速前進,k - m 步后就會相遇,相遇之處就是環的起點了。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        //獲取首次相遇時候,slow的位置
        while(fast!= null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                break;
            }
        }
        //如果快指針走到盡頭,沒環
        if(fast == null || fast.next == null) return null;
        //快指針重新出發,相遇位置就是入口位置
        fast = head;
        while(fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}
//之所以要在第一個循環結束之后再判斷if(fast == null || fast.next == null) return null;
//是因為可能存在鏈表中只有一個值,那么相當於根本沒有進行第一個循環
//這行就是為了處理這個特殊的可能性

3.尋找鏈表的中點(尋找鏈表中點的一個重要作用是對鏈表進行歸並排序)

我們還可以讓快指針一次前進兩步,慢指針一次前進一步,當快指針到達鏈表盡頭時,慢指針就處於鏈表的中間位置。
當鏈表的長度是奇數時,slow 恰巧停在中點位置;如果長度是偶數,slow 最終的位置是中間偏右:

class Solution {
    public ListNode middleNode(ListNode head) {
        if(head==null){return null;}
        ListNode fast,slow;
        fast=slow=head;

        while(slow!=null){
            if(fast.next==null){
                return slow;
            }
            if(fast.next.next==null){
                return slow.next;
            }
            slow=slow.next;
            fast=fast.next.next;
        }
        return null;

    }
}

以上是我的代碼,雖然是分類討論了,但要注意分類討論完成之后還是要找共性,盡量合並的寫出

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

4.尋找鏈表倒數第k個元素

思路還是使用快慢指針,讓快指針先走 k 步,然后快慢指針開始同速前進。這樣當快指針走到鏈表末尾 null 時,慢指針所在的位置就是倒數第 k 個鏈表節點(為了簡化,假設 k 不會超過鏈表長度):

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode former = head, latter = head;
        for(int i = 0; i < k; i++)
            former = former.next;
        while(former != null) {
            former = former.next;
            latter = latter.next;
        }
        return latter;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM