赞
踩
声明:我的链表OJ系列是针对无头单向不循环链表的
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
快慢指针是一种常用的技巧,用于检测链表中是否存在环。
这种方法的原理基于两个指针以不同的速度遍历链表:一个指针每次移动一步(称为慢指针或乌龟),另一个指针每次移动两步(称为快指针或兔子)。
如果链表中存在环,那么快指针最终会追上慢指针,两个指针会在环内的某个节点相遇。如果链表中没有环,那么快指针将会先到达链表的尾部(即NULL
),此时就可以判断链表没有环。
下面是这个思路的详细步骤:
NULL
),说明链表中没有环,因为快指针的速度是慢指针的两倍,如果快指针已经遍历完整个链表,慢指针最多只遍历了链表的一半,而链表仍然未结束,说明存在环之外的部分。true
表示链表有环。false
表示链表无环。这个方法的时间复杂度是O(n),其中n是链表的长度,因为快指针最多遍历整个链表一次。空间复杂度是O(1),因为我们只使用了两个指针变量。
代码实现
- bool hasCycle(struct ListNode* head) {
- // 如果链表为空或者只有一个节点,那么肯定没有环
- if (head == NULL || head->next == NULL) {
- return false;
- }
-
- // 使用快慢指针来检测链表是否有环
- // 慢指针每次移动一步,快指针每次移动两步
- struct ListNode* slow = head; // 慢指针,每次移动一步
- struct ListNode* fast = head->next; // 快指针,初始时指向第二个节点,每次移动两步
-
- // 当快慢指针不相遇时,继续循环
- while (slow != fast) {
- // 如果快指针已经到达链表末尾(或者快指针的下一个节点到达链表末尾),说明链表没有环
- if (fast == NULL || fast->next == NULL) {
- return false;
- }
-
- // 慢指针移动一步
- slow = slow->next;
-
- // 快指针移动两步
- fast = fast->next->next;
- }
-
- // 如果快慢指针相遇,说明链表有环
- return true;
- }
这段代码使用了快慢指针的方法来检测一个链表中是否有环。快指针每次移动两步,慢指针每次移动一步。如果链表中存在环,那么快慢指针最终会在环内的某个节点相遇;如果链表中没有环,那么快指针会先到达链表的末尾(即fast == NULL
或fast->next == NULL
),这时就可以判断链表没有环。
这种方法的时间复杂度是O(n),其中n是链表的长度。在最坏的情况下,即链表恰好形成一个环时,快指针会在遍历链表的一半长度时与慢指针相遇。空间复杂度是O(1),因为只使用了两个指针变量。
为什么快指针每次走两步,慢指针走一步可以?
假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。
当慢指针刚进环时,可 能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。
此时,两个指针每移动 一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前, 快指针肯定是可以追上慢指针的,即相遇。
快指针一次走3步,走4步,...n步行吗?
我们可以假设:快指针每走三步,慢指针每次走一步,此时快指针肯定先进环,慢指针后来才进环。
假设慢指针进环时,快指针的位置如图所示:
此时按照上述方法来绕环移动,每次快指针走3步,慢指针走1步,是永远不会相遇的,快指针刚好将慢指针套圈了,因此不行。
只有快指针走2步,慢指针走一步才可以,因为换的最小长度是1,即使套圈了两个也在相同的位置。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。