目录
1. 链表中环的入口节点
1.1 环形链表Ⅰ
1.1.1 题目描述
1.1.2解题思路
1.1.3 扩展问题
1.2 环形链表Ⅱ
1.2.1 题目描述
1.2.2 思路分析
1. 链表中环的入口节点
在leetcode上的剑指offer专栏没有收录这道题目,但Leetcode上是有这道题目的,环形链表||:
142. 环形链表 II - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle-ii/
在讲解的过程中我会先讲环形链表|,环形链表|| 是在环形链表| 的基础上完成解题的。
141. 环形链表 - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle/
1.1 环形链表Ⅰ
原题链接:
141. 环形链表 - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle/
1.1.1 题目描述
给你一个链表的头节点 head ,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
1.1.2解题思路
这道题的解题思路和“链表中的倒数第k个节点”的解题思路是相似的。快慢指针的问题,我们维护两个指针slow,fast,初始化为链表的头结点。然后我们让slow向后走一步,而fast向后走两步,如果说链表不存在环,那么fast会最终会指向NULL,此时我们结束循环返回false;
如果链表中存在环,因为fast走得快,slow走得慢,当slow刚进入环指向环内节点时,fast已经走了若干圈环,指向环上的某一个节点,此时slow和fast之间会有一段距离x (x>=0)。
因为fast走得比slow快一步,每一回合slow与fast之间的节点个数都会减一,所以在环内fast在与slow相遇之前一直在追slow,无论在slow进入环时,fast与slow相距多少个节点最终都会相遇,故当slow与fast相遇时即可判断链表中有环。
下面以环形链表1->2->3->4->5->6->7->8->9->10->4来举例分析:
bool hasCycle(struct ListNode *head) {
//指针初始化
struct ListNode *slow = head, *fast = head;
while(fast != NULL && fast->next != NULL)
{
//fast向后走两步
fast = fast->next->next;
//slow向后走一步
slow = slow->next;
//如果slow和fast相遇,有环
if(fast == slow)
{
return true;
}
}
return false;
}
1.1.3 扩展问题
(1):在slow走一步,fast走两步的条件下有环就一定会相遇吗?
(2):如果slow走一步,fast走三步行不行?
如果slow走一步,fast走四步呢?
答:(1):根据上面解题的分析在slow走一步,fast走两步的条件下,有环必定相遇。
(2):slow走一步,fast走三步,以及slow走一步fast走四步均不一定能相遇,证明过程如下:
因为环中的节点个数是不确定的,我就直接画一个圆来代表链表中的环哈。
我们把的得到的结论往Leetcode上去测试,发现slow走一步,fast走三步也是可以通过的,对于本题leetcode上的测试用例似乎并没有考虑到这一点。
bool hasCycle(struct ListNode *head) {
//指针初始化
struct ListNode *slow = head, *fast = head;
while(fast != NULL && fast->next != NULL && fast->next->next != NULL)
{
//fast向后走三步
fast = fast->next->next->next;
//slow向后走一步
slow = slow->next;
//如果slow和fast相遇,有环
if(fast == slow)
{
return true;
}
}
return false;
}
1.2 环形链表Ⅱ
原题链接:
142. 环形链表 II - 力扣(LeetCode)https://leetcode.cn/problems/linked-list-cycle-ii/
1.2.1 题目描述
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
1.2.2 思路分析
直接上结论:初始化一个指针:meet,让他指向在环形链表Ⅰ中slow与fast相遇的位置,再初始化一个指针:cur,让他指向链表的头结点,然后让cur和meet指针以相同的速度(一次循环走一步)往后走,最终他们会在环形链表的入口处相遇。
嘶,第一次看到这个结论的我是一脸懵啊。但是这个结论是有严格的证明滴,现在我们就来证明证明。
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;
struct ListNode* cur = head;
while(cur != meet)
{
cur = cur->next;
meet = meet->next;
}
return cur;
}
}
return NULL;
}