前言
第一弹的链表题目比较基础,下面两道题目难度升级,可以先自己挑战一下,再来看解析。解析有图示和的文字,有助于你的理解。
1. 环形链表
(1)题目及示例
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
示例1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环
(2)解法
在环形链表中,从头结点开始出发,会进入到一个循环,不会停下来。我们可以用快慢指针来解决这个问题,慢指针走一步,快指针走两步。
如下图所示,因为它们两个指针没走一次,之间的距离就减小一步,并且它们之间的距离是整数,一是任何整数的因子,所以fast指针一定能追上slow指针。
如何写成代码呢?我们可以写一个while循环,每次slow指针走一步,fast指针走两步,但是判断条件是什么?应该是该链表不是环形链表的情况。
- 当有偶数个结点时,fast指针会直接到空指针,
- 如果是奇数个结点的话,fast指针会走到空指针的前一个结点,再走的话就没跨过空指针。
所以判断的条件就是fast或者fast的next指针不为空。
bool hasCycle(struct ListNode *head)
{struct ListNode* slow = head, *fast = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;if (slow == fast){return true;} }return false;
}
(3)深入思考
我们上面已经证明了快指针走两步是一定可以追上的,那快指针一次走3步,走4步,一直到n步行吗?下面我将分析fast指针一次走3步的情况:
当N是奇数时,我们假设环形链表有C个结点。
所以会有一次追上了,两次追上了,还有永远追不上的情况。如果是fast指针一次走四步,也可以这么分析,只不过情况变复杂了。
结论:fast指针一次走大于两步,不一定追得上slow指针。
2.环形链表II
(1)题目及示例
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
示例1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例3:
输入:head = [1], pos = -1
输出:返回null
解释:链表中没有环
(2)思路详解
在阐述解题思路之前,我们先想一下:
- 当slow指针进入环形链表的时候,fast指针在环中能走几圈?
- 当slow指针进入环形链表的时候,fast指针需要走几圈,它们才能相遇?
第一个问题,从头结点到环形链表的第一个结点的长度,和环形链表的长度是随机的,所以可能出现slow指针还没进入环形链表时,fast指针在环形链表中已走了一圈以上的情况。
第二个问题,当slow指针进入环形链表中,不管fast指针走了几圈,基本都在slow指针的前面。我们想一想,slow指针走完一圈,fast指针至少走两圈以上,并且fast指针还在slow指针的前面,所以slow指针在走第一圈的时候,一定被追上。
如上图所示,我们知道了slow指针和fast指针走过的距离,并且我们知道slow指针一次走一步,fast指针一次走两步,就有了这样的等式关系:
2(L+X) = L+X+nC (n >=1)
化简后得到:L = nC - X (n = 1,2,3,4……)
我们假设n = 1,于是就有了L = C - X。也就是说,两个指针分别从头结点和相遇结点开始,一次走一步,当他们两个指向的地址相同时,这就是环形链表的第一个结点。那n不是1呢?结论也成立,只不过从相遇结点开始的指针需要再环中走上一圈以上。代码如下:
struct ListNode *detectCycle(struct ListNode *head)
{struct ListNode* slow = head, *fast = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;if (slow == fast){//相遇struct ListNode* meet = slow;//利用公式while(meet != head){meet = meet->next;head = head->next;}return meet;}}return NULL;
}
总结
这两道题做下来,会开拓自身的思路,可以自己推导一下上面的证明,巩固所学。话不多说,练起来!
创作不易,希望这篇文章能给你带来启发和帮助,如果喜欢这篇文章,请留下你的三连,你的支持的我最大的动力!!!