本文详细介绍了带环链表的判断与入环结点的查找,对算法进行剖析
主要采用了快慢指针,数学运算等方法将问题简化
编译语言:c
目录
判断链表是否带环
题目:
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
算法解析
方法:
使用fast slow快慢指针
slow走一步 fast走两步
如果slow 和fast可以相遇,则判断为带环链表
解析:
接下来分析原因
假设slow进环时与fast的距离为n
slow走一步,fast走2步
因为在一个环里,每循环一次这个过程slow和fast的距离相当于缩小了1
当缩小到0就是追上了
异议:
这时候会想如果fast走3步,走4步是不是也可以呢
当为3时
两指针的距离变化为
x
x-2
x-4
这时x的距离的奇偶就需要考虑了
如果为偶那么到0则追上了
如果为奇就会减为-1重新一轮追击,且距离变为c-1(设c为环的长)
如果这时候c-1为偶那么追上了
如果为奇则又需要重新来一轮再次判断
这时会发现3倍会增加判断步骤
并不理想
同理可以验证4等
代码完成
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* }li;
*/
bool hasCycle(struct ListNode *head) {
struct ListNode * s=head;
struct ListNode * f=head;
if(f==NULL)
{
return false;
}
while((f->next!=NULL)&&(f!=NULL))
{
s=s->next;
f=f->next->next;
if(f==NULL)
{
return false;
}
if(s==f)
{
return true;
}
}
return false;
}
查找带环链表的入环结点
判断是否带环问题解决了之后,查找带环链表的入环结点也就很容易了
算法解析
设头结点到入环结点的距离为L
相遇结点到入环结点的距离为N
slow走的路程为L+N
fast走的路程为L+x*C+N
fast走的路程为slow走的2倍
则有
2*(L+N)=L+x*C+N
L+N=x*C
L=x*C-N
根据式子
不难看出当此时的slow结点与头结点一起走相遇时的位置则为
入环结点
代码完成
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode * s=head;
struct ListNode * f=head;
if(f==NULL)
{
return false;
}
while((f->next!=NULL)&&(f!=NULL))
{
s=s->next;
f=f->next->next;
if(f==NULL)
{
return false;
}
if(s==f)
{
struct ListNode * h=head;
while(h!=s)
{
h=h->next;
s=s->next;
}
return h;
}
}
return false;
}
本文结束