首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【数据结构与算法】之龟兔赛跑算法

【数据结构与算法】之龟兔赛跑算法

作者头像
艾伦耶格尔
发布2025-08-28 15:28:26
发布2025-08-28 15:28:26
1040
举报
文章被收录于专栏:Java基础Java基础

本文主要介绍以环形链表相关的算法,主要采用Java语言实现,这类算法又称Floyd's Tortoise and Hare Algorithm (Floyd 龟兔赛跑算法),不得不说,这个原理挺神奇的就解决了,环形链表的问题。

注意:以下代码有关链表的算法实现均基于以下链表节点类:

代码语言:javascript
复制
//链表节点类
public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

龟兔算法动画来自:

Floyd's Hare and Tortoise Algorithm Demo - One Step! Code

相关教程:

一、龟兔赛跑算法

如果链表上存在环,那么在环上以不同速度前进的两个指针必定会在某个时刻相遇(如上图所示)。算法分为两个阶段。

1.1 阶段1

  • 龟一次走一步,兔子一次走两步
  • 当兔子能走到终点时,不存在环
  • 当兔子能追上龟时,可以判断存在环

存在环情况:(上帝视角)

不存在环情况:(上帝视角)

1.2 阶段2

  • 从它们第一次相遇开始,龟回到起点,兔子保持原位不变
  • 龟和兔子一次都走一步
  • 当再次相遇时,地点就是环的入口

a.第一次相遇:(证明已经是环)

b.龟回到起点,兔子保持原位不变:

c.再次相遇:再次相遇的位置为环入口

!!!!!!

1.3 为什么呢?

********精髓之处********

  • 设起点到入口走 a 步(本例是 7),绕环一圈长度为 b(本例是 5),
  • 那么从起点开始,走 a + 绕环 n 圈,都能找到环入口
  • 第一次相遇时
    • 兔走了 a + 绕环 n 圈(本例 2 圈) + k,k 是它们相遇距环入口位置(本例 3,不重要)
    • 龟走了 a + 绕环 n 圈(本例 0 圈) + k,当然它绕的圈数比兔少
    • 兔走的距离是龟的两倍,所以龟走的 = 兔走的 - 龟走的 = 绕环 n 圈
  • 而前面分析过,如果走 a + 绕环 n 圈,都能找到环入口,因此从相遇点开始,再走 a 步,就是环入口
  • 实在不理解的话,可以理解为快慢指针,遵循以上结论

知晓了龟兔算法的精髓后,下来解决这两个经典问题!

二、判断是否有环

2.1 问题描述

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

2.2 示例

示例一:

代码语言:javascript
复制
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例二:

代码语言:javascript
复制
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例三:

代码语言:javascript
复制
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

2.3 代码实现

代码语言:javascript
复制
public boolean hasCycle(ListNode head) {
    ListNode h = head; // 兔
    ListNode t = head; // 龟
    while (h != null && h.next != null) {
        t = t.next;
        h = h.next.next;
        if(h == t){  // 龟兔相遇时,证明是环
            return true;
        }
    }
    return false;
}

三、找到环入口

3.1 问题描述

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

3.2 示例

示例一:

代码语言:javascript
复制
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例二:

代码语言:javascript
复制
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例三:

代码语言:javascript
复制
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

3.3 代码实现

代码语言:javascript
复制
public ListNode detectCycle(ListNode head) {
    ListNode t = head; // 龟
    ListNode h = head; // 兔
    while (h != null && h.next != null) {
        t = t.next;
        h = h.next.next;
        if (h == t) {  //证明是环
            t = head;  //龟回到起点
            while (true) {
                if (h == t) { //再次相遇即为环入口
                    return h;
                }
                h = h.next; //同时前进
                t = t.next; //同时前进
            }
        }
    }
    return null;
}

总结

本文主要介绍了链表算法中经典问题:龟兔赛跑算法,主要是判断链表中是否有环,以及如何找到环的入口。最重要的是龟兔赛跑算法的两个阶段和背后的原理。并提供了Java代码实现。希望对各位有所帮助,下期见,谢谢~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、龟兔赛跑算法
    • 1.1 阶段1
    • 1.2 阶段2
    • 1.3 为什么呢?
  • 二、判断是否有环
    • 2.1 问题描述
    • 2.2 示例
    • 2.3 代码实现
  • 三、找到环入口
    • 3.1 问题描述
    • 3.2 示例
    • 3.3 代码实现
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档