赞
踩
中等
给定一个链表的头节点 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
或者链表中的一个有效索引进阶:你是否可以使用 O(1)
空间解决此题?
- 首先,我们定义了两个指针 slow 和 fast,它们都初始化为链表的头节点 head。接着,我们使用一个循环来进行遍历,直到链表结束或者快指针 fast 遇到了空节点。
- 在每一轮循环中,慢指针 slow 前进一步,快指针 fast 前进两步。如果链表中存在环,那么快指针 fast 最终会追上慢指针 slow,二者会相遇。
- 当快指针 fast 和慢指针 slow 相遇时,我们重新定义两个指针 index1 和 index2,并将它们分别指向链表的头节点 head 和相遇点。然后,我们同时以相同的速度移动这两个指针,直到它们相遇。此时,它们相遇的节点就是环的起始节点。
- 最后,如果链表不含有环,那么快指针 fast 会先遇到空节点,此时我们返回 NULL 表示链表中不存在环。
如果有环,如何找到这个环的入口
- 当快慢指针相遇时,我们可以确定链表中存在环。假设此时快慢指针都在环中的某一个位置,记为节点 A。
- 接下来,我们需要找到环的入口。根据快慢指针的原理,在相遇点 A 处,慢指针 slow 移动的距离为 L1,快指针 fast 移动的距离为 L1+L2,其中 L1 是链表头到环入口的距离,L2 是环长减去 L1 的长度。
- 我们可以使用另外一组快慢指针来寻找环的入口。让一个指针从头节点开始,另一个指针从相遇点 A 开始,两个指针都以相同的速度前进,直到它们相遇。这个相遇点就是环的入口。
- 为什么这个方法可行呢?因为当第一个指针走到环的入口时,它走了 L1 步,而第二个指针从相遇点 A 出发时也走了 L1 步。又因为第二个指针的速度是第一个指针的两倍,因此第二个指针已经在环中绕了 n 圈(n 可能为 0)。设环的长度为 C,则第二个指针走了 L1+nC 步。
- 而第一个指针此时走了 2L1 步。因此,我们有:
- 2L1 = L1 + nC
- 即:
- L1 = nC
- 这意味着,如果我们让两个指针都从头节点出发,并以相同的速度前进,一个指针走了 L1 步之后会到达环的入口,另一个指针从相遇点出发走了 L1 步之后也会到达环的入口。因此,它们会在环的入口相遇。
O(n)
时间复杂度为 O(N),其中 N 是链表的节点数。在最坏的情况下,需要遍历整个链表才能确定是否存在环以及找到环的起始节点。
O(1)
空间复杂度为 O(1),因为只使用了常数个额外的指针变量来存储中间结果,并没有使用额外的动态分配内存空间。无论链表的长度如何,所需的额外空间都保持不变。
- /**
- * Definition for singly-linked list.
- * struct ListNode {
- * int val;
- * ListNode *next;
- * ListNode(int x) : val(x), next(NULL) {}
- * };
- */
-
- class Solution {
- public:
- ListNode *detectCycle(ListNode *head) {
- ListNode *slow = head; // 慢指针,每次前进一步
- ListNode *fast = head; // 快指针,每次前进两步
-
- // 寻找环的起始节点
- while (fast != NULL && fast->next != NULL) {
- fast = fast->next->next; // 快指针每次前进两步
- slow = slow->next; // 慢指针每次前进一步
- if (slow == fast) { // 相遇,存在环
- ListNode *index1 = fast; // 设置index1指针指向相遇点
- ListNode *index2 = head; // 设置index2指针指向链表头部
- // index1和index2以相同速度移动,直到再次相遇,即为环的起始节点
- while (index1 != index2) {
- index1 = index1->next;
- index2 = index2->next;
- }
- return index2; // 返回环的起始节点
- }
- }
- return NULL; // 链表不含环,返回NULL
- }
- };
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。