算法热门:环形链表I(LeetCode 141)
Posted 白龙码~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法热门:环形链表I(LeetCode 141)相关的知识,希望对你有一定的参考价值。
你可能看过这道题的题解,但不一定看过对它深入的证明,而今天我的任务就是这个;
我们知道,链表拥有两种形态:单链表以及循环链表,其中,单链表指的是:链表仅有一个数据存储变量va和一个指向下一个结点的指针,而循环链表在单链表的基础上加了一个指向上一节点的指针;而我们今天所讨论的环形链表,不仅囊括了循环链表这种链接头尾的情况,还囊括了其它带环的情况——
情况分析
我们知道,循环链表的构成是这样的——
它的环是连接头尾的大环
而环形链表的环除此之外也可以是——
也就是说,链表中某两个结点相链接,构成了一个环,此外,带环的链表是单链表;
题目分析
我们来看具体题目:
这是我们需要完善的函数体
可以看到,题中的pos实际上是主函数部分的变量,用来形成一个环形链表,而对于只要完善函数的OJ题,由于函数的参数列表没有pos,并且返回值是一个bool值,所以我们不需要考虑pos这个变量。
那么所有的问题都集中在一个上面:如何判断链表是否带环?
最容易想到的是哈希表的思想,也就是说:每次都让指针走一个结点,然后判断这个节点之前是否出现过,不过对于绝大部分初学者而言,哈希表仅有耳闻而未接触,所以我们今天需要另想一种方法;
我们先简化一个环形链表的模型:
经历过前面的刷题我们发现,快慢指针法在链表OJ题中经常出现,那么这题我们是否也能采取快慢指针的方法呢?假设慢指针一次走一个结点,快指针一次走两个结点,那么快指针必然会在慢指针之前走到链表环的入口点;
当快慢指针继续走下去的时候,慢指针也会进入环,而快指针此时已经在环内走了N圈了;
当这种循环再进行下去,fast指针如果追上slow指针,就能判断出,链表有环;
问题来了,fast指针真的会追上slow指针吗?
证明
我们假设链表的头到环的入口的距离为L,环的长度为C,slow进入环后,与fast之间的距离为X;由于fast指针一次走两个节点,slow一次走一个节点,也就是说,fast指针比slow指针多走一步,那么如果它们继续走下去,则fast指针必然会追上slow;
如果还不理解,我们不妨把这样的一个追逐问题化为一条直线上的追逐问题:
假设还需走n次fast追上slow,那么也就是说:
解出来:n=x
也就是说,只要这样的循环再进行x次,fast必然会追上slow;
如果链表没有环,那么由于fast走得快,slow永远不可能追上它,从而我们可以判定:环存在!
代码实现
bool hasCycle(struct ListNode *head) {
struct ListNode* slow=head,*fast=head;
while(fast && fast->next)//由于fast一次走两个结点,那么需要保证fast和fast的next都不为空
{
fast=fast->next->next;//快指针走两个结点
slow=slow->next;//慢指针走一个
if(fast==slow)//快慢指针相遇
{
return true;
}
}
return false;
}
思考
当快指针一次走两结点,慢指针一次走一节点,那么快指针必然会追上慢指针;如果是其他情况呢?比如快指针走三个节点,甚至四个、五个、n个……
我们讨论快指针走三个结点的情况:
同样是这个图,slow与fast之间相差x
由于fast指针与slow指针的步长差为2,所以他们之间的距离变化为:
X-2
X-4
X-6
…
X-2N
当X是偶数时,X-2N可以为0,那么它们最终也可以相遇;
当X为奇数时,我们再看一下这个图:
当slow刚走到环入口时,slow指针走了L,fast指针走了nC+C-X,其中,n表示fast指针在slow入环之前在环内绕了n圈;又因为fast指针一次走两个节点,那么就满足:
如果X为奇数,那么(n+1)C就要是奇数,则n+1和C都要是奇数(偶数与任何数相乘都为偶数)
回到上面的X-2N的式子:
由于X为奇数,那么X-2N所得到的最小正整数为1,即:
当fast和slow之间的距离再次为X时,假设slow走了m次,则需要满足:
由于m小于C,也就是说,slow没走几步,就在一圈之内被fast又追上了,此时他们之间的距离为X,这就又回到了一开始的那样,然后又要X-2,X-4…X-N,如此循环往复,fast永远追不上slow!
小结
创作不易,留个点赞评论再走吧~关注博主,分享更多学习、刷题经验
以上是关于算法热门:环形链表I(LeetCode 141)的主要内容,如果未能解决你的问题,请参考以下文章