回环链表:回环链表属于单链表的一种,其最后的节点指向的下一个节点并非为NULL,而是指向该单链表的当中的一个节点,基于这个性质,回环链表的循环是死循环,没有终点的。如下图
对于回环链表,在面试的时候,会针如何判断一个单链表是否是回环链表并说出思路进行提问,或者提问如何找出一个回环链表它进入循环时的节点并说明思路,因此下面会分两部进行思路说明。
思路所用的方法:快慢指针(慢指针一次移动一步时,快指针一次移动两步)
如何判断一个单链表是否是循环链表:
结论:fast指针与slow指针重合
为什么是快慢指针?
因为快慢指针各自的特性,如果在非循环链表中,那么快指针一定会比慢指针先到达末节点,但因为循环链表没有末节点,所以快指针是不可能结束对节点的访问的,这样就排除了链表为非循环链表的可能性,但我们要判断一个链表是否为循环链表总得要至少一个结果进行判断得出该链表为循环链表,而我们现有的数据中,fast节点和slow节点的重合就足够满足循环链表的判断
我们可以把这个循环链表的大致过程抽象成如下的样子
因为快慢指针移动的特性,在图中从fast到slow之间的距离C-N时,假设双方又进行了一次移动,那么slow节点移动到下一个节点,但fast节点移动两个节点,这时从fast到slow的距离变为了C-N-1,相比较之前的C-N少了一个距离。
那么以此类推,随着移动次数的不断增多,那么C-N的距离会不断缩小直至0,此时代表着fast与slow的指针指向的节点是重合的,就能得出这个链表是循环链表了。
拓展:那么如果是慢指针一次走一步,快指针一次走三步呢?甚至快指针一次四步呢?
我们可以通过一次三步的演算过程,得出一次四步乃至以上的演算过程
如图(右图是计算,先看下面的再回来看计算)
与快指针一次走两次的算法差不多,C-N开始再移动一次,变成(C-N)-2,再移动变成(C-N)-4......以此类推,若(C-N)是偶数(不算入0,若为0快慢指针已经重合了),那么随着移动,两者的距离会变成0,这样就会重合。
若(C-N)为奇数,那么便会产生快指针跨过慢指针的情况,因为快指针是一次移动三步,慢指针是一次移动一步,差距是两步,因为不会重合,所以加上重合的节点,重合节点的next下一个节点就是快指针的节点,此刻快指针的后面就是慢指针,而若此刻从快指针顺时针到慢指针的话,此刻之间的距离会变成C-1,但因为每次移动都是减少两个距离节点,因此此时只要判断C-1是否是偶数就行,若是,就会有重合的时候,若不是,便永远也不会重合了。
但实际结果是,不存在C-1是奇数,也就是C是偶数的同时,(C-N)是奇数的情况,若C是偶数,(C-N)是奇数,那么N必须是奇数才行,但结合上述fast算式,若C是偶数时,N一定也是偶数,此时就会发生结果冲突,得出C是偶数时,(C-N)可以是奇数的结果不成立。
以下是leetcode上判断链表是否是循环链表的题目
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
if(head == NULL)
{
return false;
}
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast->next != NULL && fast->next->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
{
return true;
}
}
return false;
}
//这是leeccode上判断链表是否是循环链表的题目
如何找出一个循环链表的循环开始节点:
结论:当快慢指针重合后的节点再移动的节点量和从头节点开始一步一步移动的节点指针所指向的节点相同时,这个指针所指向的节点就是循环节点。
在这个图中,从头节点到循环节点之间的距离就是L,因此我们只需要求出meet节点与头节点之间的关系关系式就可以了,根据fast得出的公式可以看出,若fast指针只走了循环一圈,即x = 1,那么L = C-N,从图可知就是从头节点开始一步一步移动的指针与meet指针一步一步移动的指针所指向的节点相同时,就是循环初始节点了。若x>1,说明fast指针不止走了一圈,但因为x只影响C也就是只影响循环圈数,并不影响slow节点与fast节点重合时的meet指针所指向的节点位置,因此结果还是从头节点开始一步一步移动的指针与meet指针一步一步移动的指针所指向的节点相同时,为循环初始节点的结果了。
这是leetcode上找出循环链表的循环节点的题目
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
if(head == NULL)
{
return false;
}
struct ListNode* meet = NULL;
struct ListNode* newhead = head;
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast->next != NULL && fast->next->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
{
meet = slow;
while(meet != newhead)
{
meet = meet->next;
newhead = newhead->next;
}
return newhead;
}
}
return false;
}