环形链表问题


环形链表问题

环形链表就是一个链表没有结束的位置,链表的最后一个节点它会指向链表中的某一个节点形成一个环。

拿力扣的两到题目来看

1.环形链表

题干

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

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

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

测试用例1

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

测试用例2

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

思路

快慢指针,如果链表是有环的那么它必然不会结束。定义一个快指针fast和一个慢指针slowfast一次走2步,slow一次走1步,如果没有环fast就会走到NULL,如果有环fast就会追上slow

在这里插入图片描述

代码

bool hasCycle(struct ListNode *head) {
    if (head == NULL || head->next == NULL)
    {
        return false;
    }
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while (fast != NULL && slow != NULL && slow->next != NULL)
    {
        fast = fast->next;
        slow = slow->next->next;
        if (fast == slow)
        {
            return true;
        }
        
    }

    return false;
}

延申问题

  1. 为什么slow走一部,fast走两步,它们一定会再环里相遇,会不会永远追不上?

    不会出现追不上的情况,如果链表又换是一定能追上的。假设slow进环的时候,fast和slow的距离是 N N N,紧接着追击的过程中,fast往前走两步,slow走一步,它们每走一次,它们之间的距离都是缩小1的。

    N N N

    N − 1 N-1 N1

    N − 2 N-2 N2

    . . . ... ...

    2 2 2

    1 1 1

    0 0 0,距离缩小到0的时候就会相遇。

在这里插入图片描述

  1. slow走1步,fast走3步?走4步?走n步行不行?

    假设slow进环的时候,fast和slow的距离是 N N N,接着进行追击的时候,fast向前走3步,slow向前走1步,它们每走一次,它们之间的距离缩小2。那么就会分为两种情况。

    假设红色部分是slow入环,fast和slow的距离

在这里插入图片描述

1.如果 N N N为偶数

假设红色部分距离为10,也就是说距离 N = 10 N = 10 N=10,再追击过程中,fast走3步slow走1步,它们的距离是每次缩小2的。

10 10 10

10 − 2 10-2 102

10 − 4 10-4 104

. . . ... ...

2 2 2

0 0 0

如果 N N N为偶数,fast走3步slow走一步是可以相遇的

2.如果N为奇数

假设红色部分距离为11,也就是说距离 N = 11 N = 11 N=11,再追击过程中,fast走3步slow走1步,它们的距离是每次缩小2的。

11 11 11

11 − 2 11-2 112

11 − 4 11-4 114

. . . ... ...

11 − 10 11-10 1110

− 1 -1 1 ,当距离为 − 1 -1 1的时候fast和slow的差距就变成了 C − 1 C-1 C1, C C C为环的长度。那么这个时候 C − 1 C-1 C1恰好也是奇数,那么fast就永远也追不上slow了

总结

fast走多步,slow走一步时不可行的。

如果slow进环时,上图红色部分的距离也就是fast要追slow的距离 N N N为奇数,且环的长度为偶数,那么fast和slow就会一直在环里面打转,永远追不上。

如果距离 N N N为奇数,环的长度为奇数,fast走多部则会出现跳过slow的情况,也可能会相遇的。

2. 环形链表 II

题干

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

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

不允许修改 链表

测试用例1

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

测试用例2

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

思路

这一道题相较于上一道题的区别就是,一个判断是否有环一个时找到环的入口点。这就是上一个题目的一个变形。

  • 设起点道入口点的距离为 X X X
  • 设相遇点道入口点的距离为 Y Y Y
  • 设环的大小为 C C C

在这里插入图片描述

那么慢指针slow走的路程就是 X + Y X+Y X+Y

快指针走的路程比较特殊,如果起点道入口的距离 X X X比较长,而环的大小 C C C又比较小,那么fast可能会在环里走好几圈,假设走了 N N N圈,所以快指针fast走的路程为 X + Y + N ∗ C X+Y+N*C X+Y+NC

因为快指针fast走的路程是慢指针slow的2倍,所以最后的公式就是

2 ∗ ( X + Y ) = X + N ∗ C + Y 2*(X+Y) = X+N*C+Y 2(X+Y)=X+NC+Y

化简之后就得出这么一个公式

X = N ∗ C − Y X = N*C-Y X=NCY

假设环比较大,fast刚走完一圈就和slow相遇那么 N = 1 N = 1 N=1,起点到入口点的距离就等于环的大小减去入口点到相遇点的距离。 X = C − Y X = C-Y X=CY

那么当两个指针相遇时就可以将其中一个指向起点位置,让它们同时走一步,就会在入口点相遇。

代码实现

struct ListNode *detectCycle(struct ListNode *head) 
{
    if (head == NULL || head->next == NULL)
    {
        return NULL;
    }
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while (fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
        {
            fast = head;
            while (fast != slow)
            {
                fast = fast->next;
                slow = slow->next;
            }

            return fast;
        }
    }
    
    return NULL;
}

题目来源力扣环形链表


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的三毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值