rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 快慢指针 (fast and slow pointer)

  • 判断链表是否有环, 找环入口
    • 数学证明(极简版,一看就懂)

快慢指针 (fast and slow pointer)

判断链表是否有环, 找环入口

public class CycleLinkedListTest {

    public static class ListNode {
        String val;
        ListNode next;

        public ListNode(String val) {
            this.val = val;
        }
    }

    public static  boolean hasCycle(ListNode head) {

        if (head == null || head.next == null) {
            return false;
        }

        ListNode slow = head;
        ListNode fast = head;

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

        return false;
    }

    public static ListNode detectCycleEntry(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;

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

    public static void main(String[] args) {
        // 构建链表 A→B→C→D→B
        ListNode A = new ListNode("A");
        ListNode B = new ListNode("B");
        ListNode C = new ListNode("C");
        ListNode D = new ListNode("D");

        A.next = B;
        B.next = C;
        C.next = D;
        // 形成环
        D.next = B;

        // 判断是否有环
        boolean hasCycle = hasCycle(A);
        System.out.println("是否有环:" + hasCycle); // true

        // 找环入口
        ListNode entry = detectCycleEntry(A);
        if (entry != null) {
            System.out.println("环的入口节点是:" + entry.val); // B
        }
    }
}

数学证明(极简版,一看就懂)

假设:

  • 头节点到环入口:a 步
  • 环入口到相遇点:b 步
  • 相遇点回到环入口:c 步

快慢指针第一次相遇时:

  • 慢指针走了:a + b
  • 快指针走了:a + b + n*(b+c)(多绕了 n 圈)

因为快指针速度 = 2 倍慢指针:

2(a + b) = a + b + n(b+c)
化简:a = n(b+c) - b
      a = (n-1)(b+c) + c

结论:

头节点到环入口的距离 a = 相遇点绕环回到入口的距离 c

所以必须:

  1. slow 回到起点
  2. fast 从相遇点继续一步一步走
  3. 两者同时前进,再次相遇的地方就是环入口