参考链接:单链表中判断是否有环以及得出环的入口点(简单易懂)
1. 判断单链表是否有环
这是很多公司入门级的面试笔试题。单链表由于每个结点只有一个next指针指向下一个结点,不存在其他指针,所以一旦进入环里,就再也出不去了。类似于下图(像一个烤盘)
那么怎么样判断单链表是否有环呢?方法很简单,把环看作操场,两位选手在操场上赛跑,一个速度快,一个速度慢,如果他们一直跑,快的选手肯定会在第一次超过慢的选手之后再次追上慢的选手,与他相遇;若现在没有环,那么快的选手将始终跑在慢的选手前面,不可能有第二次相遇。所以我们判断一个链表是否有环的方法就是基于这个思想:
在链表头部设两个指针,一个每次向后移动两个位置(快指针),另一个每次向后移动一个位置(慢指针)。这相当于在起点设置了两位跑步选手,跑的快的选手的速度是慢的选手的两倍。接下来两个指针在遍历链表过程中,如果快指针到达链表尾还没有和慢指针相遇,说明链表无环,反之说明有环。
2. 获得环的入口点
书上说的公式挺复杂的,我们这里只要考虑两个指针第一次相遇的情况就好了。第一次相遇的条件是:快指针在环内比慢指针多走了一圈。如下图所示,两个指针从起点O出发,在环中的B点相遇,现在我们的目标是如何根据B点找到A点这个结点。注意:结点在环中的前进方向是顺时针,即A→B→A.
毫无疑问,两个指针第一次在B点相遇时:
慢指针走过的路程是:
S_slow = |OA| +|AB| = x + y.
快指针走过的路程是:
S_fast = |OA| + |AB| + |BA| + |AB| = x + y + z + y.
又因为快指针的速度是慢指针的两倍,所以在相同时间内快指针走过的路程是慢指针的两倍,所以
S_fast = 2 * S_slow
即
2(x + y) = x + y + z + y.
求得
z = x.
所以说明
|BA| = |OA|.
所以在两个指针相遇后,将慢指针移到O点起始位置,即链表头指针位置,快指针仍然在B点。然后它们一起向前移动,每次移动一个位置,由于|BA| = |OA|, 所以他们最终肯定会在A点相遇,A点这个相遇点就是环的入口点。
具体实现代码如下:
#include<stdio.h>
#include<malloc.h>
using namespace std;
typedef struct LNode{
int data;
struct LNode *next;
}LNode;
void CreatelistR(LNode *&head,int arr[],int n);//尾插法创建带头结点的单向链表
void ShowList(LNode *head);
LNode * Search(LNode *head,int x);
LNode *HasCircal(LNode *head);//判断是否有环,无返回nullptr,有则返回快慢结点相遇地址
LNode *FindEnter(LNode *head);//寻找环入口点地址
int main(){
LNode *head = nullptr;
int arr[] = {7,789,112,16,18,23,126};
CreatelistR(head,arr,sizeof(arr)/sizeof(arr[0]));
ShowList(head);
//构造一个单向有环链表
LNode *p = Search(head,112);//设置环入口点
LNode *end = Search(head,126);
if(p != nullptr && end != nullptr)
end->next = p;
if(HasCircal(head) != nullptr)
puts("有环");
else
puts("无环");
LNode *node = FindEnter(head);
if(node != nullptr)
printf("环入口结点为:%d\n",node->data);
else
puts("无环");
return 0;
}
LNode *FindEnter(LNode *head){
LNode *node = HasCircal(head);//相交的点
if(node == nullptr)
return nullptr;
LNode *slow,*fast;
slow = head;
fast = node;
while(slow != fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
LNode *HasCircal(LNode *head){
if(head == nullptr || head->next == nullptr)
return nullptr;
LNode *slow,*fast;
slow = head->next;
fast = head->next->next;
while(slow != nullptr && fast != nullptr){
if(slow == fast)
return slow;
slow = slow->next;
if(fast->next == nullptr)
return nullptr;
fast = fast->next->next;
}
return nullptr;
}
LNode * Search(LNode *head,int x){
if(head == nullptr)
return nullptr;
LNode *p = head;
while(p != nullptr){
if(p->data == x)
{
return p;
}
p = p->next;
}
return nullptr;
}
void CreatelistR(LNode *&head,int arr[],int n)//尾插法
{
if(arr == nullptr || n < 0)
return;
head = (LNode *)malloc(sizeof(LNode));
head->next = nullptr;
head->data = arr[0];
LNode *temp = head;
for(int i=1;i<n;i++)
{
LNode *node = (LNode *)malloc(sizeof(LNode));
node->data = arr[i];
node->next = nullptr;
temp->next = node;
temp = temp->next;
}
}
void ShowList(LNode *head){
if(head == nullptr)
return;
while(head != nullptr)
{
printf("%d ",head->data);
head = head->next;
}
puts("");
}