当前位置:   article > 正文

力扣日记8.18&8.21【链表篇】环形链表、环形链表II

力扣日记8.18&8.21【链表篇】环形链表、环形链表II

力扣日记:【链表篇】环形链表、环形链表II

日期:2023.8.18,2023.8.21
参考:代码随想录、力扣

141. 环形链表

题目描述

难度:简单

给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:
在这里插入图片描述

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

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
  • 1
  • 2
  • 3

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
  • 1
  • 2
  • 3

提示:

  • 链表中节点的数目范围是 [0, 10^4]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

题解

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode *fast = head;
        ListNode *slow = dummyHead;
        while (true) {
            if (fast == NULL || fast->next == NULL) {
                return false;
            } else if (slow == fast) {
                return true;
            }
            fast = fast->next->next;
            slow = slow->next;
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

执行结果

时间复杂度:O(n)
空间复杂度:O(1)

思路总结

  • 其实这道题是看了 环形链表II 的题解后再来做的,因此想到用了快慢指针的方法来做
  • 定义两个快慢指针,快指针比慢指针每次多走一步。如果有环的话,由于快慢指针进入环后一定是在环中转圈,所以两者会在环内相遇。即如果有环的话,两者会相遇,否则没有环。
  • 具体还是看下面 环形链表II 的思路。

142.环形链表II

题目描述

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

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

不允许修改 链表。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

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

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

题解

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
#define SOLUTION 2
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
#if SOLUTION == 1 // 哈希表(O(n), O(n))
        unordered_map<ListNode *, int> cnt;
        ListNode *cur = head;
        while (true) {
            cnt[cur]++;
            if (cnt[cur] > 1) { // 遇到第二遍
                return cur;
            }
            if (cur == NULL) { // 遇到尾节点
                return NULL;
            }
            cur = cur->next;
        }
#elif SOLUTION == 2 // 快慢指针(O(n),O(1))
        // 先找出是否有环
        ListNode *fast = head;
        ListNode *slow = head;
        while (fast != NULL && fast->next != NULL) {
            fast = fast->next->next;
            slow = slow->next;
            if (fast == slow) { // 如果快慢指针相遇了
                // 记录相遇位置
                ListNode *index1 = fast;
                // 定义从头节点出发的指针
                ListNode *index2 = head;
                // 两指针逐步后移,如果两者相遇,则为环的入口(可数学证明)
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return NULL;
#endif
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

复杂度

  1. 哈希表
    时间复杂度:O(n)
    空间复杂度:O(n)

  2. 快慢指针法
    时间复杂度:O(n)

    快慢指针相遇前,指针(慢指针)走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n

    空间复杂度:O(1)

思路总结

  • 代码随想录这次的讲解真的很好,建议直接看(绝对不是我偷懒不想记笔记了!!!……好吧就是)
  • 一开始做的时候,完全没想到用快慢指针判断是否有环(更不说找到入口了),只能拙劣地用哈希表遍历判断,结果就是空间复杂度超过O(1)
  • 快慢指针几个注意的点:
    • 快慢指针一定是在环里面相遇:如果没有环,链表是直线,快指针永远在慢指针前面。有了环,快指针最后会在环里面转圈,慢指针也是,因此一定是在环里相遇。
    • 快指针比慢指针每次多走一个节点,以确保一定会相遇,而不会直接跳过:快指针相对慢指针每次多走一个节点,即相当于快指针是一个节点一个节点靠近slow的
    • 如何找到环的入口,参考代码随想录,通过定义x、y、z
      • 在这里插入图片描述

      • 相遇时慢指针走的路程为:x+y

      • 快指针走的路程为:x+y+n(y+z). 其中n是快指针多走的圈数,n>=1

      • 由于时间相同,而快指针速度是慢指针2倍,由s/v=t得

      • (x+y+n(y+z))/2=x+y

      • 可得:x = z + (n-1)(y+z)

      • 上式的意义在于:从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。这也是代码中找到入口的依据。

  • 还是卡哥讲得清楚些
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号