力扣第142题 环形链表II c++教懂你循环的套路

题目

142. 环形链表 II

中等

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

力扣第142题 环形链表II c++教懂你循环的套路_第1张图片

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

示例 2:

力扣第142题 环形链表II c++教懂你循环的套路_第2张图片

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

示例 3:

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

提示:

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

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

思路和解题方法

  1. 首先,我们定义了两个指针 slow 和 fast,它们都初始化为链表的头节点 head。接着,我们使用一个循环来进行遍历,直到链表结束或者快指针 fast 遇到了空节点。
  2. 在每一轮循环中,慢指针 slow 前进一步,快指针 fast 前进两步。如果链表中存在环,那么快指针 fast 最终会追上慢指针 slow,二者会相遇。
  3. 当快指针 fast 和慢指针 slow 相遇时,我们重新定义两个指针 index1 和 index2,并将它们分别指向链表的头节点 head 和相遇点。然后,我们同时以相同的速度移动这两个指针,直到它们相遇。此时,它们相遇的节点就是环的起始节点。
  4. 最后,如果链表不含有环,那么快指针 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),因为只使用了常数个额外的指针变量来存储中间结果,并没有使用额外的动态分配内存空间。无论链表的长度如何,所需的额外空间都保持不变。

c++ 代码

 ​
/**
 * 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
    }
};

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

你可能感兴趣的:(leetcode,数据结构,c++,leetcode,算法,链表)