环形链表问题总结——快慢指针

环形链表问题是快慢指针的一个典型应用。其实,称其为“有环链表”更为准确。

在有环链表中,利用快慢指针可以解决的问题包括:

判断链表是否有环、求环的入口、求环的长度、求链表的总长度(总节点数),等。


目录

一 判断链表是否有环

二 有环链表的入口节点

三 有环链表中环的长度

3.1 方法一:第一次相遇后继续走,直到第二次相遇

3.2 方法二:先求出环的入口节点

四 有环链表的总长度


具体步骤:

初始,slow和fast都从链表头节点head开始,fast每次走2步,slow每次走1步。如果fast走到链表尾部,说明链表无环;否则,链表有环,fast和slow必定在环中的某个节点第一次相遇。【判断是否有环get

第一次相遇后,令fast重新指向head,slow不变。二者重新开始以相同速度移动,则第二次相遇时的节点必是环的入口节点。【环的入口get】在此过程中,若记录下fast移动的步数则是链表中非环部分的长度LengthLine。【非环的长度get

已知环的入口节点后,令一个指针绕环走一圈,就得到了环的长度LengthLoop。【环的长度(2) get】LengthLine + LengthLoop即为链表的总长度。【链表总长度get

如果只求环的长度,还有另一种方法:fast和slow第一次在环中相遇后,以此作为起点,fast仍然每次走2步,slow每次走一步。当两个指针第二次在环中相遇时,fast恰好比slow多走了一圈。因此用fast走过的步数减去slow走过的步数,则为环的长度。【环的长度(1) get

 

                       

 


Java代码如下:

一 判断链表是否有环

力扣:环形链表 I

//快慢指针
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                return true;
        }
        return false;
    }
}

二 有环链表的入口节点

力扣:环形链表 II

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        ListNode slow = pHead, fast = pHead;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                break; 
        }
 
        //无环:
        if(fast == null || fast.next == null) 
            return null;
 
        //有环:
        fast = pHead;
        while(slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

三 有环链表中环的长度

3.1 方法一:第一次相遇后继续走,直到第二次相遇

    //求环的长度 (方法一:第一次相遇后继续走,直到第二次相遇)
    public static int LengthOfLoop(ListNode head) {
        ListNode slow = head, fast = head;
        while(head != null && head.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                break;
        }
        //无环
        if(fast == null || fast.next == null)
            return -1;

        //有环
        int countSlow = 0, countFast = 0;
        while(true) {
            slow = slow.next;
            fast = fast.next.next;
            countSlow += 1;
            countFast += 2;
            if(slow == fast)
                break;
        }
        return (countFast - countSlow);
    }

3.2 方法二:先求出环的入口节点

    注:此函数的输入为环的入口节点

    //求环的长度(方法二:先求出环的入口节点enter)
    //... ...
    //... ...
    public static int LengthOfLoop2(ListNode enter) {
        int count = 1;
        ListNode cur = enter.next;
        while(cur != enter) {
            count++;
            cur = cur.next;
        }
        return count;
    }

四 有环链表的总长度

分别求出链表中 线性部分的长度LengthLine、环形部分的长度LengthLoop,二者相加即为链表总长度。

    //求有环链表总长
    public static int LengthOfList(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                break;
        }
        fast = head;
        int lengthLine = 0;   //非环部分的长度
        while(slow != fast) {
            slow = slow.next;
            fast = fast.next;
            lengthLine++;
        }
        System.out.println("LengthLine: " +lengthLine);
        //此时fast和slow都指向环的入口节点
        int lengthLoop = 1;  //环的长度
        fast = fast.next;
        while(fast != slow) {
            fast = fast.next;
            lengthLoop++;
        }
        System.out.println("LengthLoop: " +lengthLoop);
        return (lengthLine + lengthLoop);
    }

       如果觉得有用就点个赞吧~

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