一、给定一个链表,判断链表是否带环。
leetcode刷题链接:141. 环形链表 - 力扣(LeetCode)
解题思路:(快慢指针法)
①.首先如果一个链表不带环,那么在遍历这个链表,一定会走到NULL。
②.如果链表带环,则在遍历这个链表会出现死循环,遍历一个链表通常的方法是用一个指针从头开始向后遍历,很显然用一个指针解决不了这个问题。
③.那么,如果有一点刷题经验的人,会想到,既然一个指针解决不了,那么双指针呢,毕竟在我们的刷题的时候,双指针是一个比较常用的方法,所以这个时候尝试双指针能不能解题。
④.当我们用指针去遍历一个环形链表的时候,无论怎么遍历都遍历不完,就好比如一个人在一个死循环的路里面走,如果是双指针,那就是两个人在死循环里面走,这时重点来了,假设这个死循环就是一个跑道,两个速度不同的人在跑道里走,那么他们肯定会相遇,即两个不同速度的指针在链表里遍历,如果链表带环,则这两个指针会相遇。
如图所示:
用一个慢指针从头开始,一个快指针从头的下一个开始,每次慢指针走一步,快指针走两步,如果链表带环,那么这两个指针肯定会相遇,否则快指针会先走到NULL。
解释:如果链表带环,那么快慢指针都会进入环,在环中,慢指针每次走一步,快指针每次走两步, 从相对运动角度来说,可以看成慢指针不动,快指针每次走一步,那么在环中,快指针一定会遇上慢指针。
思考:如果快指针每次走三步,四部行不行?
不行。
因为如果快指针走三步,那么相对慢指针来说,每次走两步,那么就会有可能快遇上的时候,直接越过去。
leetcode解题代码
bool hasCycle(struct ListNode *head)
{
if(head==NULL || head->next==NULL)//如果链表没有节点或只有一个节点,直接返回。
{
return false;
}
struct ListNode* slow=head;//慢指针指向头
struct ListNode* fast=head->next;//快指针指向头的下一个
while(slow!=fast)//当慢指针不等于快指针
{
if(fast==NULL || fast->next==NULL)//如果快指针走到空,则返回假
{
return false;
}
else
{
slow=slow->next;//慢指针走一步
fast=fast->next->next;//快指针走两步
}
}
return true;
}
二、给定一个链表,如果带环,则返回入环的第一个节点
leetcode刷题链接:142. 环形链表 II - 力扣(LeetCode)
解题思路:
①.首先要判断链表是否带环,带环才能找第一个入环的结点。判断带环参考第一题。
②.用一个指针从头开始遍历,另一个指针从上题中快慢指针相遇的位置开始遍历,两个指针每次走一步,则它们会在入环的第一个结点相遇。
如图所示:
证明:为什么这样做可以得到结果?
①.设链表不带环部分的长度为a,环的周长为c,相遇点的后半部分为b,则相遇点前半部分为c-b。
②.首先,无论这个链表的环形部分多长多短,不是环形部分多长多短,在快慢指针相遇时,慢指针的路程绝对不会超过一圈。如下图所示:(在这个链表的情况下,快慢指针不会出现这样的情况,即不会是在这些位置,只是用来演示极限情况)
③.在慢指针入环之前,快指针可能已经走了n圈。
④.所以得到路程表达式:
慢指针:S1=a+(c-b)
快指针:S2=a+nc+(c-b)
⑤.快指针路程是慢指针的两倍。
⑥.所以得到:2*S1=S2
⑦.带入表达式得: 2*a+2*(c-b)=a+nc+(c-b)
a+c-b=nc
a=n(c-1)+b
这个公式表示:一个指针cur从头出发,slow指针从相遇点出发, 两个指针每次都走一步,当cur走a步走到入环口的时候,slow指针从相遇点处走了n圈加上b,即入环处。
leetcode解题代码
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode* fast=head;//快慢指针都从头出发
struct ListNode* slow=head;
while(fast!=NULL && fast->next!=NULL)
{
slow=slow->next;//慢指针走一步
fast=fast->next->next;//快指针走两步
if(fast==slow)
{
break;
}
}
if(fast==NULL || fast->next==NULL)//如果快指针走到NULL,说明不带环,返回空
{
return NULL;
}
fast=head;//快指针从头开始走,每次一步
while(fast!=slow)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}