前言
1.如果在单向链表中有环如何检测?
2.如何知道环的长度?
3.如何知道环外长度,或者说是由链表头结点到环碰撞点的长度?
4.如何知道整条链的长度?
5.如果存在环又该如何消除环?
问题1:如何检测单向链表的环路问题?
想象一下跑跑道,如果是直线,大家速度不一样是肯定追不上的,但是看比赛的时候不难发现,对于长程比赛而言,速度快的是可以追上速度慢的人的,也就是套圈,那么也就是基于此,我们可以来分析单链表,就相当于是奥运会,马拉松在外面跑了一会儿最后还是会回到主操场中一样。我们可以假设一个跑步慢的人,每次一步,一个跑的快的人,每次两步,那么如果跑的快的人追上了跑的慢的人那么就是当前的路径中存在环,如果跑的慢的跑完了全程也没再次遇到跑的块的人,那么就不存在环路。
考虑代码逻辑:
起始位置:都是head;如果有环,一定不会有NULL,如果没环,fast指针一定会先一步走到链表尽头,这就是循环的结束条件;如果在移动的过程中相遇,那么就可以推断出有环并退出循环。
#include <iostream>
using namespace std;
typedef struct ListNode
{
int value;
ListNode* pNext;
}Node;
//创建无环单链表
Node* creatList(int n)
{
Node* head = new Node;
head->value = 0;
head->pNext = NULL;
Node* pre = head;
for(int i = 1; i < n; i++)
{
Node* node = new Node;
node->value = i;
node->pNext = NULL;
pre->pNext = node;
pre = pre->pNext;
}
return head;
}
//打印单链表内容
void display(Node* head)
{
while(head != NULL)
{
cout<<head->value<<endl;
head = head->pNext;
}
}
//查看是否存在环
bool SearchCycle(Node* head)
{
Node* fast = head;
Node* slow = fast;
bool result = false;
while(fast != NULL && fast->pNext != NULL)
{
slow = slow->pNext;
fast = fast->pNext->pNext;
if(slow == fast)
{
result = true;
break;
}
}
return result;
}
//创建环路
void creatCycle(Node* head)
{
Node* pre = head;
Node* next = pre->pNext;
while(next->pNext != NULL)
{
pre = pre->pNext;
next = next->pNext;
}
next->pNext = pre;
}
int main()
{
int n = 5;
Node* head = creatList(n);
display(head);
cout<<SearchCycle(head)<<endl;
creatCycle(head);
cout<<SearchCycle(head)<<endl;
return 0;
}
我的代码比较简单,就可以简单的判断有没有环。以及创建一个环后再次判断是否存在环。那么如果单链表中存在这样的环应该如何解决呢?
问题2:环的长度
这里需要考虑的是何时两人会再次相遇,因为它们都已经陷入到环中,是不会再回到单向的直线跑道上去的,那么假设甲每次2步,在相遇时一共跑了n次,那么对于相同的n次运动,乙每次一步,一共跑了n,即甲跑的距离为2n,乙跑的距离为n,那么甲比乙多跑的距离应为2n - n,假设相遇的圈数为k,而每圈的距离为m,那么就可以得出 2n - n = k · m,当k = 1时,即他们在之后第一次相遇,n = m,即 乙行进的距离刚好是一圈,所以可以通过统计乙行进的步数来得出环的长度。
int CycleLength(Node* head)
{
Node* fast = head;
Node* slow = fast;
int step = 0;
while(fast != NULL)
{
if(fast == slow)
{
do
{
step++;
slow = slow->pNext;
fast = fast->pNext->pNext;
}while(fast != slow);
break;
}
else
{
fast = fast->pNext->pNext;
slow = slow->pNext;
}
}
return step;
}
问题3:碰撞点到连接点的距离
碰撞点p到连接点的距离 = 头指针到连接点的距离 = 环外长度。
问题4:整条链表的长度
整条长度 = 环外长度 + 环长度
问题5:如果单链表中存在环,如何去掉这个环
如果slow的下一个结点位置刚好是fast,说明fast又绕回去了,那么就使slow的pNext = NULL,即可。