链表与环的三个问题:判断是否有环,求环的长度,求环的起点

本文介绍了如何解决链表中环的三个关键问题:使用快慢指针判断链表是否存在环;求解环的长度,通过让快慢指针相遇后继续前进直至再次相遇,它们的步数差即为环长;确定环的起点,通过公式推导,找到特定位置开始的指针将在环内相遇于起点。理解这些方法需要结合图解辅助思考。

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

1. 判断单链表是否有环

两个指针,一个快一个慢,快指针每次都每次走两步,慢指针每次走一步,如果两个指针相遇了,则说明有环,遇到NULL,则说明无环。


2. 求单链表中环的长度

还是原来的快慢指针,假设他们在p点相遇,相遇之后继续走,下一次相遇的的时候,快指针比慢指针多走的距离正好是环的长度。


3. 求单链表中环的起点

这个稍微复杂一下,我们假设链表的起点位点h,环的起点为点a,环的长度为l,上面的快慢指针在p点相遇。

继续假设相遇的时候,快指针绕着环走了m圈,慢指针绕着环走了n圈,那么:

慢指针走的长度:(h-->a) + (a-->p) + n*l

快指针走的长度:(h-->a) + (a-->p) + m*l

则 2 * ( (h-->a) + (a-->p) + n*l ) = (h-->a) + (a-->p) + m*l

化简一下得到:(h-->a) + (a-->p) + 2*n*l = m*l

(h-->a) + (a-->p)  = (m-2*n)*l

(h-->a) = (m-2*n)*l - (a-->p)

说明假如点x从链表的起点开始,点y从p点开始走,每次都走一部,当x走到环的起点a时,y正好也到环的起点a,因为正好是环的倍数差了(a-->p)嘛,而y正好是从p点出发的所以正好是会到环的起点a。

说得比较抽象,可以画个图看看。

<think>嗯,用户想知道怎么检测单向链表里的,还要找到入的第一个节点。这个问题我之前好像学过,但得仔细想想怎么一步步解决。 首先,检测的存在,常见的办法应该是快慢指针吧。快指针每次走两步,慢指针每次走一步。如果有的话,它们肯定会相遇。这点我记得是对的,因为如果没,快指针会先走到末尾,而如果有,快指针会在里绕圈,最终和慢指针碰上。 那怎么确定入节点呢?这时候可能得用数学方法推导。假设相遇时,慢指针走了k步,快指针走了2k步。外的长度是L,长度是C,相遇点距离入点的距离是m。那么,慢指针里走了k-L步,也就是m的位置。而快指针已经绕了n圈,所以2k = L + nC + m。然后因为快慢指针相遇时,快指针比慢指针多走了一圈?或者可能需要用L和C的关系来找。这时候可能需要把其中一个指针移回到起点,然后两个指针都每次走一步,再次相遇的点就是入点。这个步骤的依据是什么? 比如,当快慢指针相遇后,把慢指针留在原地,快指针回到头节点,然后两个指针都每次走一步,再次相遇的位置就是入点。或者是不是其中一个指针回到头节点?这时候可能需要重新计算步数。比如,当第一次相遇时,L = (n-1)C + (C - m),所以当两个指针再次以相同速度走时,会在入点相遇。 还有,空间复杂度要O(1),所以不能用哈希表存储访问过的节点。所以必须用快慢指针法。 那具体步骤应该是这样的:先判断是否,如果有,再找入点。在判断后,将一个指针重置到头节点,然后两个指针同步移动,直到相遇点即为入点。 比如,引用中的资料提到,快慢指针相遇后,如何找到切入点。根据引用2,快慢指针法需要一些步骤来找到入点。可能这时候需要数学推导。比如,当第一次相遇时,头节点到入点的距离是A,入点到相遇点的距离是B,的剩余部分是C。则相遇时,慢指针走了A + B,快指针走了A + B + nC。而快指针的速度是慢指针的两倍,所以2(A+B) = A + B + nC → A + B = nC → A = nC - B。这时候,如果让一个指针从头开始走A步,另一个从相遇点走,剩下的nC - B步刚好等于A,所以相遇点就是入点。 所以步骤应该是: 1. 快慢指针找到相遇点,确认有。 2. 然后,将其中一个指针(比如快指针)放回头节点,然后两个指针每次各走一步,再次相遇的点就是入点。 这样就能满足空间复杂度O(1)的要了。 那么,具体的实现代码该怎么写呢?比如,在Python中,先初始化快慢指针,然后循直到相遇,如果有的话。然后重置一个指针到head,再同步移动直到相遇。这时候返回该节点的值即可。 需要注意边界情况,比如链表为空或者只有一个节点的情况。这时候直接返回-1,因为没有的可能。 比如,如果链表为空或只有一个节点,直接返回-1。否则,快慢指针初始化为head和head.next?或者都是head?要看具体实现。比如,初始化slow = head,fast = head.next,然后循的条件是fast和fast.next不为空。这样可能更快检测到相遇。 或者在开始的时候,先判断head是否为null或者head.next为null,这时候直接返回-1,因为没有的可能。否则,slow和fast都从head开始,每次slow走一步,fast走两步。 举个例子,假设链表结构为1->2->3->4->2(在2的位置)。这时候,快慢指针会在某个点相遇,比如节点4或者节点3?然后按照上述方法,重置一个指针到head,然后同步走,直到相遇在节点2。 这样就能正确找到入点了。 总结下来,步骤就是快慢指针检测,找到相遇点后,再通过同步移动找到入点。这应该就是正确的方法,符合空间复杂度O(1)的要。</think>### 检测单向链表中的及定位入节点的方法 #### 一、检测是否存在 使用**快慢指针法**(Floyd判圈算法): 1. 初始化两个指针$slow$和$fast$,均指向链表头节点。 2. $slow$每次移动1步,$fast$每次移动2步。 3. 若链表,$fast$会先遇到空节点。 4. 若链表,$fast$最终会追上$slow$(两者相遇)[^3]。 #### 二、定位入节点 1. 当快慢指针相遇后,将$fast$重新指向链表头节点,保持$slow$在相遇点。 2. 两个指针改为每次均移动1步。 3. 当$fast$和$slow$再次相遇时,相遇点即为入节点[^2]。 #### 三、数学推导 假设: - 头节点到入节点的距离为$L$。 - 的周长为$C$。 - 第一次相遇时,$slow$移动了$L + m$步,$fast$移动了$L + m + nC$步($n$为整数)。 - 根据速度关系:$2(L + m) = L + m + nC \implies L = nC - m$。 - 因此,从头节点移动$L$步,从相遇点移动$nC - m$步,最终会在入节点重合。 #### 四、代码实现(Python) ```python class ListNode: def __init__(self, val): self.val = val self.next = None def detect_cycle(head): if not head or not head.next: return -1 slow = head fast = head # 检测是否存在 has_cycle = False while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: has_cycle = True break if not has_cycle: return -1 # 定位入节点 fast = head while fast != slow: fast = fast.next slow = slow.next return slow.val ``` #### 五、示例分析 以链表$1 \to 2 \to 3 \to 4 \to 2$(在节点2)为例: 1. 快慢指针在节点4相遇。 2. 将$fast$重置到头节点1,同步移动后: - $fast$移动路径:$1 \to 2$。 - $slow$移动路径:$4 \to 2$。 3. 最终在节点2相遇,即入节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值