要求:
- 不允许修改链表结构
- 时间复杂度o(n),空间复杂度o(1)
判断是否有环:
如果链表有环,那么在遍历时则会陷入死循环。
- 使用快慢指针
- 快指针移动2步,慢指针移动1步
- 如果走到某一步,快慢指针相遇,则说明有环
环入口点:
我们假设链表头部到环入口距离 len, 环入口到快慢指针交汇点的距离为x,环的长度为R。环入口到快慢指针第一次交汇时,设慢指针走的长度为 d = len + x,快指针走的长度为 len + x + nR = 2d(因为快指针走2,慢指针走1),化简 len = nR - x;
- 定义两个指针cur和inter
- cur指向单链表头节点,inter指向快慢指针交汇点第一次相交的节点,cur和inter同时向后遍历,第一次相交的点就是环的入口。
- inter指针在遍历过程中可能多次(n >= 1)经过环入口节点,但当cur指针第一次达到入口节点时,inter指针此时必然也指向入口节点。
解释:链表头部到环入口的距离, 环入口到快慢指针交汇点的距离 ,加起来等于nR。相当于 链表头部->环入口->快慢指针交汇点可以形成一个环。
#include <iostream>
using namespace std;
//结点
struct Node
{
int value;
Node* next;
};
//尾插法
Node* create_list(int v, int len)
{
Node* ret = NULL;
Node* slider = NULL;
for(int i=0; i<len; i++)
{
Node* n = new Node();
n->value = v++;//头结点
n->next = NULL;
if(slider == NULL)
{
slider = n;//slider和头结点挂接
ret = n;//ret指向头结点
}
else
{
slider->next = n; //尾插法
slider = n;//移动slider指向新插入的结点
}
}
slider->next = ret->next->next->next; //成环
return ret;//返回链表头结点
}
void destory_list(Node* list)
{
while(list)
{
Node* del = list;
list = list->next;
delete del;
}
}
void print_list(Node* list)
{
while(list)
{
cout << list->value << "->";
list = list->next;
}
cout << "NULL" << endl;
}
Node* ListLoop(Node* list)
{
if(NULL == list)
return NULL;
Node* fast = list;
Node* slow = list;
while(1)
{
fast = fast->next ? fast->next : NULL;
if(NULL == fast)
break;
fast = fast->next ? fast->next : NULL;
if(NULL == fast)
break;
slow = slow->next ? slow->next : NULL;
if(fast == slow)
break;
}
if(NULL == fast)
return NULL;
Node* cur = list;
Node* inter = slow;
while(cur != inter)
{
cur = cur->next;
inter = inter->next;
}
return cur;
}
int main()
{
Node* list = create_list(1, 6);
Node* cur = NULL;
cur = ListLoop(list);
cout << cur->value << endl;
return 0;
}