文章目录


前言

哈喽,我是长路,目前刚刚大三,方向是后端也偶尔捣鼓下前端,现在的主语言是Java。之前一大段时间都是在学习web开发的一些技术,就很久没有进行类似于数据结构、算法之类的学习与刷题,打算这段时间拾起来好好学一学、搞一搞。

这段时间也是机缘巧合看到草帽路飞的博客,加了自学群,正巧看到博主组织在群里组织了leetcode刷题打卡活动,我也就参与进来,为期一个月,打算坚持每天都花一些时间做一些题目,并通过博客的方式来进行记录。

目前跟着一个Github仓库刷题(leetcode):​​代码随想录leetcode刷题​​,当前为链表专题。




题目

题目来源leetcode

leetcode地址:​​142. 环形链表 II​​,难度:中等。

题目描述(摘自leetcode):

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。

进阶:
你是否可以使用 O(1) 空间解决此题?

示例1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:
链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105
pos 的值为 -1 或者链表中的一个有效索引

本地调试代码:

class Solution {

public ListNode detectCycle(ListNode head) {
...
}

public static void main(String[] args) {
//链表A
ListNode aNode5 = new ListNode(5, null);
ListNode aNode4 = new ListNode(4, aNode5);
ListNode aNode3 = new ListNode(3, aNode4);
ListNode aNode2 = new ListNode(2, aNode3);
aNode5.next = aNode2;//设置入环节点
ListNode aNode1 = new ListNode(1, aNode2);
//调用方法
ListNode node = new Solution().detectCycle(aNode1);
System.out.println("入环节点值为:"+node.val);
}

}




题解

NO1:快慢指针

核心问题(2点)

1、为什么慢指针一定会与快指针重合相遇

快指针每次移动2步,慢指针移动1步,慢指针与快指针必会重合。不同情况的环快指针可能会走n圈,慢指针不会超过一圈。也就是说一圈内必重合。

2、求解到相遇的节点之后,如何确定指定的入环节点

这里我觉得 ​​代码随想录—题解​​中的图特别好这里引用一下:

leetcode【链表—中等】142.环形链表 II_数据结构

慢指针走的步数:​​x+y​

快指针走的步数:​​x+y+n(z+y)​​,这里z+y指的是环形一圈的意思,n指的是转了n圈。

因为快指针每次走两步,慢指针走一步,公式:​​2(x+y) = x+y+n(z+y)​​ => ​​x+y = n(z+y)​​ => ​​x = n(z+y)-y​

最终化解为:​​x = (n-1)(z+y)+z​​,​​(n-1)(z+y)​​就是指的快指针环形绕的n圈意思

此时我们可以得出一个结论,当快慢指针相遇时,x与z的值都是相等的,那么我们代码中只需要求得快慢指针重合的节点以及头节点每次移动一步进行对比即可找出环形入口节点。




思路及代码

思路:快慢指针(慢一步,快两步),找到环内重合点,接着令头节点与重合节点一起移动,每次移动一步,一旦两个指针指向的节点相同就找到入环节点。

代码

public ListNode detectCycle(ListNode head) {
//节点为null、1个节点直接返回null
if(head == null || head.next == null){
return null;
}
//快慢指针同时从头节点出发
ListNode slow = head;
ListNode fast = head;
//这里直接将快指针指向节点及下个节点不为空作为条件
while(fast != null && fast.next != null){
slow = slow.next;//慢指针移动一步
fast = fast.next.next;//快指针移动两步
//若是快慢指针相遇
if(slow == fast){
//两个指针一个指向头节点,另一个指针指向快节点
ListNode index1 = head;
ListNode index2 = fast;
//结合之前等式,我们来进行同时移动两个指针每次移动一步,一旦相等就说明找到了入环节点
while(index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;//此时返回任意一个节点即可,因为此时是index1与index2都是相同节点
}
}
return null;
}

leetcode【链表—中等】142.环形链表 II_快慢指针_02




参考文章

[1]. ​​leetcode题解​

[2]. ​​ 代码随想录—142.环形链表II​




我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习