弗洛伊德循环查找算法-原理

本文灵感来自哔哩哔哩视频 

视频链接:

弗洛伊德循环查找算法

算法代码(java)

package rain;

class ListNode {
    int value;
    ListNode next;

    public ListNode(int value) {
        this.value = value;
        this.next = null;
    }

    @Override
    public String toString() {
        return "ListNode{" +
                "value=" + value +
                '}';
    }
}
public class FloydCycleDetectionAlgrithm {
    public static void main(String[] args) {
        ListNode node0 = new ListNode(4);
        ListNode node1 = new ListNode(3);
        ListNode node2 = new ListNode(7);
        ListNode node3 = new ListNode(8);
        ListNode node4 = new ListNode(6);
        ListNode node5 = new ListNode(9);
        ListNode node6 = new ListNode(2);
        ListNode node7 = new ListNode(1);
        ListNode node8 = new ListNode(5);
        ListNode node9 = new ListNode(2);

        node0.next = node4;
        node1.next = node3;
        node2.next = node7;
        node3.next = node8;
        node4.next = node6;
        node5.next = node9;
        node6.next = node2;
        node7.next = node1;
        node8.next = node5;
        node9.next = node2;

        ListNode node = Find(node0);
        System.out.println(node);
    }
    public static ListNode Find(ListNode head) {
        ListNode slow  = head;
        ListNode fast = head;

        while (true) {
            slow = slow.next;
            fast = fast.next.next;
            //第一次相遇
            //if slow and fast meet, then break the loop
            if (fast == slow) {
                break;
            }
        }
        //第二次相遇
        slow = head;
        while (true) {
            slow = slow.next;
            fast = fast.next;
            if (fast == slow) {
                return fast;
            }
        }
    }
}

弗洛伊德循环查找算法中第二次相遇的地方就是循环的起始点,这个性质的证明是基于数学的原理。这是因为在第一次相遇时,慢指针 `slow` 和快指针 `fast` 已经处于同一个循环内。设链表起点到环的起始点的距离为 X,环的起始点到第一次相遇点的距离为 Y,第一次相遇点到环的起始点的距离为Z。

第一次相遇

 ListNode slow  = head;
        ListNode fast = head;

        while (true) {
            slow = slow.next;
            fast = fast.next.next;
            //第一次相遇
            //if slow and fast meet, then break the loop
            if (fast == slow) {
                break;
            }
        }

设循环长度

1. 在第一次相遇时,慢指针 `slow` 走的距离,

C2是常数

而快指针 `fast` 走 的距离

2. 由于快指针的速度是慢指针的两倍,所以快指针走的距离是慢指针的两倍。因此,有

3. 进一步简化上述等式,得到

这意味着

循环前的距离X = 一定数量循环次数 减去 汇合点前距离Y

4.因为

可得

C3乘以 循环长度长度l 意味着固定的循环量!

找到节点只需要让两个节点分别走X和C3l+Z的路程

此时, 我们可以把slow放在起始位置 每次走一格, 

同时 让fast也是每次走一格

直到相遇 fast == slow 说明相遇的节点就是循环开始的节点

//第二次相遇
        slow = head;
        while (true) {
            slow = slow.next;
            fast = fast.next;
            if (fast == slow) {
                return fast;
            }
        }

也就是说,如果此时将慢指针重新指向链表起始点,慢指针再次移动 X 的距离,而快指针从第一次相遇点开始移动 C3l+Z 的距离,它们将会在环的起始点再次相遇。

因此,第二次相遇的地方就是循环的起始点。这个性质是弗洛伊德循环查找算法的关键之一,也是该算法能够正确找到环的起始点的原因。

文末推荐我常用的一个学习网站 可视化运行程序

比如这段链表就可以直观展示出来

网站链接

可视化运行网站

 

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-01-19 07:26:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-19 07:26:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-19 07:26:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-19 07:26:03       20 阅读

热门阅读

  1. 使用Spring管理Caffeine缓存(CacheManager+Caffeine)

    2024-01-19 07:26:03       38 阅读
  2. 家庭家用服务全方面机器人

    2024-01-19 07:26:03       39 阅读
  3. axios的使用以及Vue动画

    2024-01-19 07:26:03       35 阅读
  4. axios原理

    2024-01-19 07:26:03       32 阅读
  5. Dynamo 使用小结

    2024-01-19 07:26:03       30 阅读
  6. spark+phoenix读取hbase

    2024-01-19 07:26:03       35 阅读
  7. 情绪价值怎么自己给自己

    2024-01-19 07:26:03       35 阅读
  8. 【排序算法】快速排序的基本算法

    2024-01-19 07:26:03       30 阅读
  9. Cmake 中list命令总结

    2024-01-19 07:26:03       37 阅读