持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情
环形链表 |
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例一:
输入: head = [3,2,0,-4], pos = 1
输出: true
解释: 链表中有一个环,其尾部连接到第二个节点。
示例二:
输入: head = [1,2], pos = 0
输出: true
解释: 链表中有一个环,其尾部连接到第一个节点。
示例三:
输入: head = [1], pos = -1
输出: false
解释: 链表中没有环。
提示:
- 链表中节点的数目范围是
[0, 104] -105 <= Node.val <= 105pos为-1或者链表中的一个 有效索引 。
解题思路:(双指针)
这道题其实就是要我们判断链表中是否有环,可以用快慢指针来实现
具体思路:
- 分别定义
fast和slow指针,同时从头节点出发,fast每次移动两个节点,指针slow每次移动一个节点。 - 如果
slow和fast在链表里碰到了,就代表链表里有环,返回true。 - 如果
fast走到了null,就代表链表没环,返回false为什么fast每次移动两个节点,slow每次移动一个节点,有环的时候,指针就一定会在环中碰到了,而不是永远的错开呢: - 首先可以肯定的是,
fast是先走进环中的,两个指针如果相遇,也一定是在环中所相遇的
为什么slow和fast一定会遇到呢:
- 我们可以画个环,让
fast在任意一个节点追slow。会发现最终都是这种情况:
slow和fast再移动一步,就会遇到了,这是因为相对于slow来讲,fast每次移动都是在不断的缩短与slow的距离,所以它们一定可以遇到
代码:(Java实现)
public boolean hasCycle(ListNode head) {
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)
提交结果
进阶:环形链表 ||
进阶的这道题比上一道题多了一个返回值,我们不单单要判断链表中是否存在环,还需要返回开始入环的第一个节点
解题思路:(双指针)
我们通过上一道题知道了如何判断链表中是否存在环,这一道题要解决的就是如何返回如环的第一个节点
我们假设头节点到环入口的节点数为X,环入口到相遇节点的节点数为Y,从相遇节点到环入口的节点数为Z
从图中可以看出指针
slow到相遇节点位置的节点数为X+Y,指针fast到相遇节点位置的节点数为X+Y+n(Y+Z),n为fast在环内走的圈次数,(Y+Z)为环内一圈的节点数
因为slow一次移动一个节点,fast一次移动两个节点。我们可以得到一个等式:
slow * 2 = fast => (X+Y) * 2 = X+Y+n(Y+Z)两边各消除一个(X+Y),得X+Y = n(Y+Z)
又因为我们要找环形的入口,所以我们需要取X,即X = n(Y+Z)-Z
再化简下,得 X = (n - 1)(Y+Z) + Z,这里的n一定大于等于1,因为fast至少要走一圈才能遇见slow
先拿n为1来举例,表明fast至少移动了1圈,才与slow碰到
当n为1时,X=Z
这就意味着:当两个指针相遇时,从头节点出发一个指针,从相遇节点出发一个指针,指针每次只移动一个节点,两个指针遇到后,它们相遇的位置就是环形的入口节点
也就是说的是,当链表里有环时,定义两个指针,index1从头节点出发,index2从相遇节点出发,两个指针每次只移动一个节点,当两个指针碰到时,返回他们所处的位置
代码:(Java实现)
public ListNode detectCycle(ListNode head) {
ListNode A = head;
ListNode B = head;
while (B != null && B.next != null) {
A = A.next;
B = B.next.next;
if (A == B) {
ListNode index1 = head;
ListNode index2 = B;
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index2;
}
}
return null;
}
复杂度分析
- 时间复杂度:O(n) 第二次相遇中,慢指针须走步数
a < a + b;第一次相遇中,慢指针须走步数a + b - x < a + b,其中x为双指针重合点与环入口距离;因此总体为线性复杂度; - 空间复杂度:O(1)