141.环形链表
题目
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪next
指针再次到达,则链表中存在环。
为了表示给定链表中的环,使用整数position
来表示链表尾连接到链表中的位置(索引从\(0\)开始)。 如果position
是\(-1\),则在该链表中没有环。
注意:position
不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回true
。否则,返回false
。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
解题思路
通过使用具有不同速度的快、慢两个指针
遍历链表,空间复杂度可以被降低至\(O(1)\)。慢指针每次移动一步,而快指针每次移动两步
。
- 如果列表中不存在环,最终快指针将会最先到达尾部,此时可以返回
false
。 - 如果列表中存在环,把慢指针和快指针想象成两个
在环形赛道上跑步的运动员
。而快跑者最终一定会追上慢跑者(最终都会在环上跑,跑的快的一定会套圈
跑得慢的)。
代码实现
/**
* 141. 环形链表
* @author chenzufeng
* @date 2021-6-27
*/
public class No141_LinkedListCycle {
public boolean hasCycle(SinglyLinkedListNode head) {
if (head == null || head.nextNode == null) {
return false;
}
// 设置快慢指针,如果有环,则快指针与慢指针一定会相遇
SinglyLinkedListNode slow = head;
SinglyLinkedListNode fast = head.nextNode;
while (slow != fast) {
// 快指针到达链表尾部
if (fast == null || fast.nextNode == null) {
return false;
}
slow = slow.nextNode;
fast = fast.nextNode.nextNode;
}
return true;
}
}
复杂度分析
-
时间复杂度:\(O(n)\)。将\(n\)设为链表中结点的总数,为了分析时间复杂度,分别考虑下面两种情况。
-
链表中不存在环:快指针将会首先到达尾部,其时间取决于列表的长度,也就是\(O(n)\)。
-
链表中存在环:将慢指针的移动过程划分为两个阶段:非环部分与环形部分:
-
慢指针在走完非环部分阶段后将进入环形部分:此时,快指针已经进入环中:\(\text{迭代次数} = \text{非环部分长度} = N\)
-
两个指针都在环形区域中:考虑两个在环形赛道上的运动员(快跑者每次移动两步而慢跑者每次只移动一步),速度的差值为1,因此需要经过\(\dfrac{\text{二者之间距离}}{\text{速度差值}}\)次循环后,快跑者可以追上慢跑者。这个距离是\(\text{环形部分长度 K}\),因此,\(\text{迭代次数}=近似于\text{环形部分长度 K}\)。
-
-
综上,在最糟糕的情形下,时间复杂度为\(O(N+K)\),也就是\(O(n)\)。
-
-
空间复杂度:\(O(1)\),只使用了慢指针和快指针两个结点,所以空间复杂度为\(O(1)\)。