掌握链表——环形问题(详解证明过程)及练习题

本文讲解了带环链表的概念,介绍了利用快慢指针判断链表环的方法,通过龟兔赛跑的比喻阐述原理,并推导了如何计算入环节点的公式。深入探讨了快慢指针速度对环检测的影响,以及不同步长情况下环的存在条件。最后提供了练习题和解决链表环问题的代码实例。
摘要由CSDN通过智能技术生成

带环链表是什么🤔?

难道是头上带发光的环吗,

在这里插入图片描述若一个链表带环,那么用指针一直顺着链表遍历,最终会回到某个地方。链表带环不是一个正常的链表,因为他会造成死循环。

判断链表是否有环

思路: 核心思想就是快慢指针,当两个指针相遇的时候说明有环。(为什么会相遇?这个后面会讲到)我们可以定义两个指针(快慢指针),两个指针均从表头开始同时遍历链表,快指针一次走两步,慢指针一次走一步。如果链表带环,那么快慢指针一定会相遇,否则快指针会先遍历完链表(遇到NULL)。😃
例如:龟兔赛跑
在这里插入图片描述

struct ListNode {
	int val;
	struct ListNode *next;
};
bool hasCycle(struct ListNode *head) {
	struct ListNode* slow = head;
	struct ListNode* fast = head;
	while (fast && fast->next)// 遍历,如果不带环那么它最后会遇到NULL,循环结束
	{
		fast = fast->next->next;//每次走两步
		slow = slow->next;  //  每次走一步
		if (fast == slow)   // 每走完一次,就进行判断是否相遇了
			return true;    // 相遇后返回,这链表带环
	}
	return false;   // 遍历完了,fast或fast->next为空,证明链表不带环
}

两个快慢指针的起始位置
在这里插入图片描述
快指针进入环的人口,慢指针到环入口还有一半的距离,
在这里插入图片描述
在这里插入图片描述

当慢指针进换时,快指针开始追击慢指针,它们间追击的距离假设为N,那么每走一次,N就要-1,(N=N+1-2)
所以一直迭代下去
N-1
N-2
N-3
……
0

0就代表快慢指针距离为0,它们相遇了,
在这里插入图片描述
小结:
1.如果链表有环,slow每次走一步,fast每次走两步,最后它们一定会相遇,是因为在追击的过程中,距离以整数1的递减,最后为0,0代表距离为0。
2.slow和fast,fast一定是先进环,这是slow走了入环前距离的一半。
3.随着slow进环,fast已经在环里面走了一段,走了多少跟环的大小有关系。

下面我们再延伸一个问题
为什么slow走一步,fast要走两步呢?能不能让fast一次走n步(n>2)?请证明一下 结论:fast一次走n步?

假设slow每次走1步,fast每次走3步,slow到入环点了,fast也在环里走了一会,现在fast开始追slow,追击的距离为N
在这里插入图片描述
他们之间的距离变化如下:
N是偶数 - --N是奇数
N ------------- N
N-2 ---------- N-2
N-4 ---------- N-4
N-6 ---------- N-6
…… --------- ……
2 ------------- 1
0 ------------ -1
N是偶数可以追上 N是奇数这一次追不上,
-1意味着他们之间的距离变成了
C-1,(C代表环的长度)

在这里插入图片描述
C也有两种情况:
C偶数
偶数-1为奇数
C-1为奇数
C-1相当于N
N-2
N-4
……
-1
接下来C-1永远是奇数
那么慢指针与快指针永远不能相遇


C为奇数
奇数-1为偶数
C-1为偶数
C-1相当于N
N-2
N-4
……
0
当等于0时那么慢指针与快指针相遇

小结:如果N是奇数,距离变成-1意味着他们之间的距离变成C-1 C-1是奇数还是偶数,取决于C的长度 如果C-1是奇数,那么永远追不上了
如果C-1是偶数,那么可以追上

练习题

题目描述:
给定一个链表,返回链表开始入环的第一个节点如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。题目来源

思路:
1.先判断链表是否有环,没环返回NULL。
2.通过一个公式(公式将在下面推导)求入环的第一个节点。
公式就是:L的距离就是入环前的距离,
假设链表有环,slow每次走一步与fast每次走两步,假设slow与fast相遇的点为meetNode
公式就是
L(距离)=meetNode到入环第一个结点的距离
如图:
在这里插入图片描述

结论:head与meetNode同时走,那么当他们相遇就是入环的第一个节点。

现在我们来推论公式:

假设slow每次走一步,fast每次走一步
当fast追上slow时,

slow走的距离为:L+X
fast走的距离为: L +X+n*c(n>=1,n为fast环的圈数)

因为fast是slow的两倍速度,所以他们的距离也是两倍
2(L+X)=L+X+nc
化简得:
L=n
c-X
L=(n-1)*c - c-X
在这里插入图片描述
(n-1)*c,不管n是多少meetNode总能回到meetNode
所以

                L=c-X
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *slow=head;
    struct ListNode *fast=head;

    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(slow==fast)
        {
            struct ListNode *meetNode=slow;struct ListNode *cur=head;
            while(meetNode!=cur)// 相等则就是环的第一个结点
            {
                cur=cur->next;
                meetNode=meetNode->next;
            }
            return meetNode;
        }
         

    }
   return NULL;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

2023框框

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

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

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

打赏作者

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

抵扣说明:

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

余额充值