📜1. 题目
给定一个链表的头节点 head,返回链表开始入环的第一个节点。 如果链表无环,则返null。
如果链表中有某个节点,可以通过连续跟踪next指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改链表。
示例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 或者链表中的一个有效索引。
🔍2. 思路
🔑2.1 链表是否带环
这里的思路十分简单,即用快慢指针,一个快指针fast每次走两步,一个慢指针slow每次走一步,如果链表是带环的,就演变成了追击问题,当慢指针进环,快指针开始追击。
🔑2.2 为何能追上
思路较好理解,但是 为何快指针一定能追上慢指针,会不会出现追不到的情况? 这还需要我们进一步求证。
我们这里设置的快慢指针,他们的速度差为1,即如果开始追击,快指针fast与慢指针slow的距离每次缩小1。
如果慢指针slow每次走一步,快指针fast每次走n步,那么还一定能够追上吗?
这就需要分情况讨论,因为每次缩小的距离不是1,可能会出现错过的情况:
题外话:
我们不也是这个环里的“快指针”嘛,都在追击着属于我们的“慢指针”。
在这个快节奏的时代,走的太快,可能会忽略很多美好的风景,最终的结果也可能不是自己想要的。
那我们不妨放慢脚步,看看我们来时的路,调整好状态再出发,一步一个脚印。
🔑2.3 入口点的确定
tips:
这里fast走的距离不可认为是L+C+X,因为环的大小不确定,可能在slow入环之前,fast已经在环里面走了几圈了:
这样我们就能得出一个结论:一个指针从相遇点走,一个指针从起始点走,会在入口点相遇。
🔓3. 代码实现
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
struct ListNode* meet = slow;
struct ListNode* start = head;
while(meet != start)
{
meet = meet->next;
start = start->next;
}
return meet;
}
}
return NULL;
}