1.题目: 链表中环的入口结点
描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围: n\le10000n≤10000,1<=结点值<=100001<=结点值<=10000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
1->2 ->3->4->5->3
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
2.算法:
双指针算法
- 时间复杂度:O(n)O(n)O(n),最坏情况下遍历链表两次
- 空间复杂度:O(1)O(1)O(1),使用了常数个指针,没有额外辅助空间
3.算法思想
那我们现在假定已经是一个有环的链表了,
那么这个链表中怎么找到环的入口呢?在慢指针进入链表环之前,快指针已经进入了环,且在里面循环,这才能在慢指针进入环之后,快指针追到了慢指针,
不妨假设快指针在环中走了n圈,慢指针在环中走了m圈,它们才相遇,而进入环之前的距离为x,环入口到相遇点的距离为y,相遇点到环入口的距离z。
快指针一共走了 x+n(y+z)+y 步,
慢指针一共走了 x+m(y+z)+y 步
这个时候快指针走的倍数是慢指针的两倍,
则 x+n(y+z)+y=2(x+m(y+z)+y),
这时候 x+y=(n-2m)(y+z) ,因为环的大小是y+z,说明从链表头经过环入口到达相遇地方经过的距离等于整数倍环的大小:那
我们从头开始遍历到相遇位置,和从相遇位置开始在环中遍历,会使用相同的步数,而双方最后都会经过入口到相遇位置这y个节点,
那说明这y个节点它们就是重叠遍历的,那它们从入口位置就相遇了,这我们不就找到了吗?
4.代码:
/*************************************************
作者:She001
时间:2022/9/25
题目: 链表中环的入口结点
描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围: n\le10000n≤10000,1<=结点值<=100001<=结点值<=10000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
1->2 ->3->4->5->3
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
示例1
输入:
{1,2},{3,4,5}
复制
返回值:
3
说明:
返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3
示例2
输入:
{1},{}
返回值:
"null"
说明:
没有环,返回对应编程语言的空结点,后台程序会打印"null"
***************************************************/
//算法思路:
/*
那我们现在假定已经是一个有环的链表了,
那么这个链表中怎么找到环的入口呢?在慢指针进入链表环之前,快指针已经进入了环,且在里面循环,这才能在慢指针进入环之后,快指针追到了慢指针,
不妨假设快指针在环中走了n圈,慢指针在环中走了m圈,它们才相遇,而进入环之前的距离为x,环入口到相遇点的距离为y,相遇点到环入口的距离z。
快指针一共走了 x+n(y+z)+y 步,
慢指针一共走了 x+m(y+z)+y 步
这个时候快指针走的倍数是慢指针的两倍,
则 x+n(y+z)+y=2(x+m(y+z)+y),
这时候 x+y=(n-2m)(y+z) ,因为环的大小是y+z,说明从链表头经过环入口到达相遇地方经过的距离等于整数倍环的大小:那
我们从头开始遍历到相遇位置,和从相遇位置开始在环中遍历,会使用相同的步数,而双方最后都会经过入口到相遇位置这y个节点,
那说明这y个节点它们就是重叠遍历的,那它们从入口位置就相遇了,这我们不就找到了吗?
*/
#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
int i;
node *next;
};
void print(node * head)//打印链表
{
node* pp= head;//复制头节点
while(pp!=NULL)//判断这个节点是否为空 链表是否结束
{
cout<<pp->i<<" ";
pp=pp->next;//指向下一个
}
cout<<endl;
}
//判断有没有环,返回相遇的地方
node* islianjie(node *head) {
//先判断链表为空的情况
if(head == NULL)
return NULL;
//快慢双指针
node* fast = head;
node* slow = head;
//如果没环快指针会先到链表尾
while(fast != NULL && fast->next != NULL){
//快指针移动两步
fast = fast->next->next;
//慢指针移动一步
slow = slow->next;
//相遇则有环
if(fast == slow)
//返回相遇的地方
return slow;
}
//到末尾则没有环
return NULL;
}
node* fangfa_1(node* pHead)
{
node* slow = islianjie(pHead);
//没有环
if(slow == NULL)
return NULL;
//快指针回到表头
node* fast = pHead;
//再次相遇即是环入口
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return slow;
}
int main()
{
//建立 第一个 单链表
node *a1=new node;
node *a2=new node;
node *a3=new node;
node *a4=new node;
node *a5=new node;
node *a6=new node;
node *a7=new node;
a1->i=1;//链表节点的复制
a2->i=2;
a3->i=3;
a4->i=4;
a5->i=5;
a6->i=6;
a7->i=7;
a1->next=a2;//链表的连接
a2->next=a3;
a3->next=a4;
a4->next=a5;
a5->next=a6;
a6->next=a7;
a7->next=a2;
//建立 第二个 单链表
node *b1=new node;
node *b2=new node;
node *b3=new node;
node *b4=new node;
node *b5=new node;
node *b6=new node;
node *b7=new node;
b1->i=1;//链表节点的复制
b2->i=2;
b3->i=3;
b4->i=4;
b5->i=5;
b6->i=6;
b7->i=7;
b1->next=b2;//链表的连接
b2->next=b3;
b3->next=b4;
b4->next=b5;
b5->next=b6;
b6->next=b7;
b7->next=b4;
//建立 第三个 单链表
node *c1=new node;
node *c2=new node;
node *c3=new node;
node *c4=new node;
node *c5=new node;
node *c6=new node;
node *c7=new node;
c1->i=1;//链表节点的复制
c2->i=2;
c3->i=3;
c4->i=4;
c5->i=5;
c6->i=6;
c7->i=7;
c1->next=c2;//链表的连接
c2->next=c3;
c3->next=c4;
c4->next=c5;
c5->next=c6;
c6->next=c7;
c7->next=NULL;
node* kk=fangfa_1(a1);
if(kk==NULL)
{
cout<<"没有环"<<endl;
}
else
{
cout<<"环的节点为: "<<kk->i<<endl;
}
node * gg=fangfa_1(b1);
if(gg==NULL)
{
cout<<"没有环"<<endl;
}
else
{
cout<<"环的节点为: "<<gg->i<<endl;
}
node * ll=fangfa_1(c1);
if(ll==NULL)
{
cout<<"没有环"<<endl;
}
else
{
cout<<"环的节点为: "<<gg->i<<endl;
}
return 0;
}