目录
1.当节点个数为偶数个,slow走到中间节点,如果fast为空,则没有环。
2.当节点个数为奇数个,slow走到中间节点,如果fast->next为空,则没有环。
步骤一:求出环内slow与fast相遇节点,当相遇时,相遇节点为fast或者slow
步骤二:创建指针newhead,记录相遇节点的下一个节点,将相遇节点的next置为NULL
步骤三:创建指针curh,curnh,变量counth,countnh,计算两个链表节点个数
思路一、
环形链表求环入口节点
一、判断是否有环
通过快慢指针来判断是否有环。
慢指针为slow,每次走一步。快指针为fast,每次走两步。都从首节点head开始。
1.当节点个数为偶数个,slow走到中间节点,如果fast为空,则没有环。
2.当节点个数为奇数个,slow走到中间节点,如果fast->next为空,则没有环。
3.如果有环,那么slow和fast一定会相遇。
因为每当slow和fast向前走时,距离都会缩进1。slow处在环入口时,与正在环中的fast相距N。
在移动的过程中,N = N-1、(N-1)-1.......1,0,直到N=0,slow和fast相遇。
4.延伸:如果fast每次走三步,四步,n步是否能相遇。
可知,当slow处在环入口节点时,fast在环中,此时相距N。
1. 如果fast走三步,那么差距步为n = 2。
如果N为偶数,那么N能被n除尽,与环的奇偶性无关,N = N - 2、(N-2) - 2 .......2、0,能相遇。
如果N为奇数,N则不能被n除尽,N = N - 2 、(N-2) - 2........1、-1,第一次fast追击上slow时,相距为1。
N不能除尽,则判断是否相遇条件由N,变为fast在环内第一次追上slow之间的距离能否被除尽。
假设环的节点数为C,fast在环内第一次追上slow,则slow与fast相距为C-1,此时判断是否相遇的条件为C-1是奇是偶,如果C-1仍然为奇数,则C-1不能被差距步n=2除尽,无法相遇。如果C-1为偶,则能被n除尽,能相遇。
2.如果fast走四步,差距步n = 3。
可知,当slow处在环入口节点时,fast在环中,此时相距N。
如果N能被n整除,那么能相遇。
如果不能,设gap = N-n、(N-n)-n、......,如果(C-gap) 能被n整除,则能相遇,以此类推。
二、计算环入口节点
1. 首先通过判断是否有环,找到相遇节点。
设头节点head到环入口节点距离为L,slow与fast相遇节点到环入口节点距离为X。环的节点数为C。
可知,slow走过的距离为:L+X。
(X的大小不会超过环的大小C,因为如果slow走了一圈,那么fast一定走了两圈多,肯定是在一圈以内与slow相遇了。)
fast走过的距离:L+n*C+X。n为圈数,C为环的大小。
(我们并不知道在slow进入环之前,fast在环内循环了多少次,如果L的距离很长,C的大小很小,那么可能fast已经循环了多次,则设n为fast循环的次数。)
联合slow与fast:2(L+X) = L+n*C+X。(因为fast走的距离是slow的两倍)
可得:L = n*C-X。
所表达的含义为,创建一个指针meet从头节点开始出发。另一个指针fast(用fast和slow都一样)从相遇节点开始出发,meet和fast每次都走一步,它们会在入口节点相遇。
L为指针meet从头节点head走到环入口节点的距离,与此同时,指针fast从相遇节点走了n*C的距离,还是在相遇节点,然后减去X,就是环入口节点,这两个节点会在环入口节点相遇。
如果meet的地址等于fast的地址,则为环入口节点。
思路二、
将环形链表求入口节点转换为两个相交链表求交点
步骤一:求出环内slow与fast相遇节点,当相遇时,相遇节点为fast或者slow
步骤二:创建指针newhead,记录相遇节点的下一个节点,将相遇节点的next置为NULL
不置空也行,则在计算两个链表的个数时,循环计数条件则变为 curh != fast, curnh != fast。
步骤三:创建指针curh,curnh,变量counth,countnh,计算两个链表节点个数
1.用curh记录head的地址,用curnh记录newhead的地址,防止在计算链表的节点个数,改变head和newhead的地址。
2.使用abs求出counth与countnh的差值的绝对值,定义变量gap存放
如果counth>countnh,让head先走gap步。
如果counth<countnh,让newhead先走gap步。
步骤四:head和newhead同时走,它们一定会相交
当head的地址等于newhead的地址,此时head或者newhead为链表的环入口节点。
三、
思路一代码实现
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* slow = head;
struct ListNode* fast = head;
//链表数为奇数则fast->next为NULL;
//偶则fast为NULL;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
//相遇节点;
if(slow == fast)
{
struct ListNode* meet = head;
//meet从头节点出发,fast从相遇节点出发;
//用fast或者slow从相遇节点出发;
while(meet != fast)
{
meet = meet->next;
fast = fast->next;
}
//相等跳出循环,返回fast或者meet;
return meet;
}
}
//没有环返回空;
return NULL;
}
思路二代码实现
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* slow = head;
struct ListNode* fast = head;
//链表数为奇数则fast->next为NULL;
//偶则fast为NULL;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
//相遇节点;
if(slow == fast)
{
//创建新链表的头节点,为相遇节点的next;
struct ListNode* newhead = fast->next;
//记录两个链表的个数,防止修改head与newhead的地址;
struct ListNode* curnh = newhead;
struct ListNode* curh = head;
//将相遇节点的next置为NULL;
//不置空也行,则计数时的判断条件为curh != fast ,curnh != fast(fast为相遇节点);
fast->next = NULL;
int counth = 0;
int countnh = 0;
//计算链表个数
while(curh)
{
curh = curh->next;
counth++;
}
while(curnh)
{
curnh = curnh->next;
countnh++;
}
//计算两个链表节点个数的差值的绝对值;
int gap = abs(countnh-counth);
//个数大的先走;
if(counth>countnh)
{
while(gap--)
{
head = head->next;
}
}
else
{
while(gap--)
{
newhead = newhead->next;
}
}
//求两个链表的交点;
while(head != newhead)
{
head = head->next;
newhead = newhead->next;
}
//相等跳出循环,返回head或者newhead;
return head;
}
}
//没有环返回空;
return NULL;
}