1、环状链表
这道题的思路是利用快慢指针方法,这个方法在我以前的博客有讲解:
链表的中间节点——方法二
有环则两个指针一定会相遇,没环则不会相遇。
代码如下 :
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
return true;
}
}
return false;
}
这道题虽然做完了,但是在面试的时候还会问延伸的问题 如:
环状链表延伸问题
问题一、为什么slow走一步,fast走两步,他们一定会在环里面相遇,会不会永远追不上?请证明!
问题二、slow走一步,fast走3步、4步、n步行不行?为什么?请证明!
问题三、求环的入口点!
问题一:
不会,假设当slow进入环的时候,跟fast的距离是N,当slow走一步,fast就走两步,此时距离为N-1,以此类推,他们每次走一次,距离就会缩小1,最后一定会缩小到0,就会相遇。
问题二:
假设fast一次走 3 步,那么他们每走一次,距离就会缩小 2,如果slow进环时,和fast的距离N为偶数,那么可以追上。如果为奇数,减到最后距离为-1。
此时距离变成 C(环的大小)-1 这时候要考虑环的大小,如果环的节点数为偶数,C-1就为奇数,还是追不上。如果环的节点数为奇数,那么就可以追上。
总结一下: 环的大小为奇数就能追上。环的大小为偶数时:如果slow进环时,与fast的距离为奇数,就不能追上,一直打圈;如果与fast的距离为偶数,就可以追上
问题三:
因为fast和slow一定会相遇,那么我们可以假设相遇点为meet
那么我们可以推出以下公式:
slow走的距离: L + X
fast走的距离:L + n* C + X
*n的原因是L的长度不确定,slow走到入口点时fast可能在环中转了好几圈了
而fast的距离时slow的两倍
所以 2*( L + X) = L + n* C + X
化简得:L = N*C - X
我们先不看N,L = C - X 就相当于一个指针从起点走,一个指针从meet沿逆时针方向走,就会在入口处相遇。
2、链表中环的入口节点
公式法
这个题就是上面的问题三,那么可以直接写代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
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* meet = fast;
while(meet != head)
{
meet = meet->next;
head = head->next;
}
return meet;
}
}
return NULL;
}
相交链表法
从meet的下一个节点处开始到入口处为一个链表,
从起点处到入口处为一个链表。这道题就被转化为相交链表问题,我在之前的博客有过讲解:第七题
图片如下,两条链表用红 绿标出:
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
struct ListNode* meet = fast;
//转化成相交链表
struct ListNode* newhead = meet->next;
int lenA = 0, lenB = 0;
struct ListNode* curA = head, *curB = newhead;
while(curA != meet)
{
lenA++;
curA = curA->next;
}
while(curB != meet)
{
lenB++;
curB = curB->next;
}
struct ListNode* longlist = head, *shortlist = newhead;
if(lenA < lenB)
{
longlist = newhead;
shortlist = head;
}
//先走的步数
int gap = abs(lenA - lenB);
while(gap--)
{
longlist = longlist->next;
}
while(longlist != shortlist)
{
shortlist = shortlist->next;
longlist = longlist->next;
}
return longlist;
}
}
return NULL;
}