判断链表是否存在环路,并找出回路起点

本文介绍了一种通过快慢指针来检测链表中是否存在环路的方法,并详细解释了如何定位环路的起始节点。利用两个速度不同的指针,一个速度快一倍于另一个,在环形链表中两指针必定会相遇。通过调整指针位置,可以精确找到环路的起点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【试题描述】定义一个函数,输入一个链表,判断链表是否存在环路,并找出回路起点

Circular linked list: A (corrupt) linked list in which a node’s next pointer points to an 
earlier node, so as to make a loop in the linked list
EXAMPLE
Input: A -> B -> C -> D -> E -> C [the same C as earlier]
Output: C

 

If we move two pointers, one with speed 1 and another with speed 2, they will end up meet-ing if the linked list has a loop Why? Think about two cars driving on a track—the faster car 
will always pass the slower one!
The tricky part here is finding the start of the loop Imagine, as an analogy, two people rac-ing around a track, one running twice as fast as the other If they start off at the same place, when will they next meet? They will next meet at the start of the next lap

Now, let’s suppose Fast Runner had a head start of k meters on an n step lap When will they next meet? They will meet k meters before the start of the next lap (Why? Fast Runner would have made k + 2(n - k) steps, including its head start, and Slow Runner would have made n - k steps Both will be k steps before the start of the loop )
Now, going back to the problem, when Fast Runner (n2) and Slow Runner (n1) are moving around our circular linked list, n2 will have a head start on the loop when n1 enters Specifi-cally, it will have a head start of k, where k is the number of nodes before the loop Since n2 has a head start of k nodes, n1 and n2 will meet k nodes before the start of the loop

So, we now know the following:
1   Head is k nodes from LoopStart (by definition)
2   MeetingPoint for n1 and n2 is k nodes from LoopStart (as shown above)
Thus, if we move n1 back to Head and keep n2 at MeetingPoint, and move them both at the 
same pace, they will meet at LoopStart

分析:为何能够找到环的起始位置?

假设环的长度是 m, 进入环前经历的node的个数是 k , 那么,假设经过了时间 t,那么速度为2 的指针距离起始点的位置是:  k + (2t - k) % m = k + (2t - k) - xm . 同理,速度为1的指针距离起始点的位置是 k + (t - k) % m = k + (t - k) - ym。

如果 k + (2t - k) - xm =  k  + (t - k) - ym ,可以得到 t = m (x - y)。 那么当t 最小为m的时候,也就是说,两个指针相聚在 距离 起始点 m - k的环内。换句话说,如果把一个指针移到链表的头部,然后两个指针都以 1 的速度前进,那么它们经过 k 时间后,就可以在环的起始点相遇。

【参考代码】

复制代码
 1     public static boolean judgeList(LinkList myList)
 2     {
 3         Link fast, slow;
 4         fast = slow = myList.first;
 5         while (true)
 6         {
 7             if (fast == null || fast.next == null)
 8                 return false;
 9             else if (fast == slow || fast.next == slow)
10                 return true;
11             else
12             {
13                 slow = slow.next;
14                 fast = fast.next.next;
15             }
16         }
17     }
复制代码

 

复制代码
 1     public static Link FindBeginning(LinkList myList)
 2     {
 3         Link fast, slow;
 4         fast = slow = myList.first;
 5         while(fast.next !=null)
 6         {
 7             slow = slow.next;
 8             fast = fast.next.next;
 9             if(slow == fast)
10                 break;
11         }
12         
13         if(fast.next == null)
14             return null;
15         /* Move slow to Head. Keep fast at Meeting Point. Each are k steps
16         /* from the Loop Start. If they move at the same pace, they must
17          * meet at Loop Start. */
18         slow = myList.first;
19         
20         while(slow!=fast)
21         {
22             slow = slow.next;
23             fast = fast.next;
24         }
25         
26         return fast;
27     }
复制代码
### 判断链表是否存在环的算法 判断链表是否存在环是一个经典的数据结构问题,通常可以通过多种方法实现。以下是几种常见且有效的解决方案。 #### 方法一:快慢指针法 快慢指针法是一种高效的方法,用于检测链表是否存在环。该方法的核心思想是定义两个指针——一个“快指针”和一个“慢指针”,它们分别以不同的速度遍历链表。具体来说,“慢指针”每次向前移动一步,而“快指针”每次向前移动两步[^1]。如果链表存在环,则这两个指针最终会在某个节点相遇;如果没有环,则“快指针”会先到达链表末尾返回 `null`。 下面是基于 Java 的快慢指针法实现: ```java public class ListNode { int val; ListNode next; ListNode(int x) { val = x; next = null; } } public 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; // 若退出循环则不存在环 } ``` 这种方法的时间复杂度为 O(n),空间复杂度为 O(1)[^3]。 --- #### 方法二:哈希集合法 另一种常用的方法是利用哈希集合记录已经访问过的节点。当遍历链表时,将当前节点存入集合中。如果发现某节点已经在集合中存在,则表明链表中有环;反之,若成功遍历至链表末端,则表示无环。 下面展示了 Python 中的实现方式: ```python class ListNode: def __init__(self, x): self.val = x self.next = None def has_cycle(head): visited_nodes = set() # 创建一个空集合作为存储已访问节点的空间 current_node = head while current_node is not None: # 只要当前节点非空就继续执行 if current_node in visited_nodes: # 发现重复节点意味着存在环路 return True visited_nodes.add(current_node) # 将新遇到的节点加入到集合当中去 current_node = current_node.next # 移动到下一个节点位置上去 return False # 完整扫描完毕之后仍然未找到任何重复项的话就可以断定是没有闭环的情况发生啦~ ``` 此方法虽然简单易懂,但由于需要额外维护一个哈希集合,因此其空间复杂度较高,达到 O(n)[^2]。 --- #### 输入与输出示例 假设我们有一个链表 `{3, 2, 0, -4}` 且知道其中确实含有环(第二个参数设为任意正数),那么调用上述任一函数都会返回布尔值 `True` 表明存在环状连接关系[^4][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值