单链表有环,是指单链表的最后一个节点的指针指向了链表中的某个节点。试编写一个算法判断单链表是否存在环。
1)给出算法的基本设计思想
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
3)说明你所设计的算法的时间复杂度和空间复杂度
1)设置快慢两个指针,命名为fast和slow。初始时两个指针都指向表头L,slow每次只走一步,也就是slow = slow->next,fast每次走两步,也就是fast = fast->next->next。由于fast比slow走得快,如果有环,那么fast指针一定先进入环而slow后进入换。当两个指针都进入环后,经过若干操作后两个指针一定能在环上相遇,这样就可以判断一个链表是否有环。
如图所示,当slow进入环的时候,fast早已进入环。fast每次比slow多走一步且fast和slow的距离小于环的长度,所以fast与slow相遇的时候,slow所走距离不超过环的长度。
如图所示,设头节点到环的入口的距离为n,环的入口点沿着环的方向到相遇点的距离为x,环长为r,相遇时fast绕过了环n圈,所以可以得出一个公式:
2
(
a
+
x
)
=
a
+
n
∗
r
+
x
2(a+x) = a+n*r+x
2(a+x)=a+n∗r+x
化简得
a
=
n
∗
r
−
x
a = n*r - x
a=n∗r−x
也就是说,我们先定义两个指针,先将一个指针p指向链表的表头L,然后将另一个指针指向相遇的点,这个时候我们会发现环的开始节点就是这两个节点共同移动,然后相遇的那个节点。
2)代码如下:
LNode* FindLoopStart (LinkList &L){
LNode *fast = L;
LNode *slow = L;
while(fast->ptr&& fast->ptr->ptr){//找到fast和slow两个指针相遇的那个节点
slow = slow->ptr;
fast = fast->ptr->ptr;
if (slow==fast)break;
}
if (!slow||!fast->ptr)return nullptr; //假设没有环,那么直接返回空指针
LNode *p = L;
LNode *q = slow;
while(p!=q){//寻找环的开始的节点
p = p->ptr;
q = q->ptr;
}
return p;
}
3)时间复杂度为O(n),空间复杂度为O(1)