法一(循环遍历):
利用快慢指针法(见#141.环形链表),判断出链表中存在环的同时计算慢指针移动次数为n,快指针移动为2n。则链表入环节点位置小于等于n,链表总位置数小于等于2n,由此建立循环。
定义两个指针iptr和jptr嵌套循环进行遍历,直到iptr与jptr相等,说明查找到了入环节点,此时直接返回iptr。
(缺点:时间复杂度较高)
代码实现(c++):
class Solution
{
public:
ListNode *detectCycle(ListNode *head)
{
if(head==nullptr) return nullptr;
ListNode* slow=head;
ListNode* fast=head;
int n=0;
while(fast->next!=nullptr&&fast->next->next!=nullptr)
{
slow=slow->next;
fast=fast->next->next;
n++;
if(fast==slow)
{
int i,j;
ListNode* iptr=head;
for(i=0;i<=n;i++)
{
ListNode* jptr=iptr;
for(j=i;j<=2*n;j++)
{
jptr=jptr->next;
if(iptr==jptr)
return iptr;
}
iptr=iptr->next;
}
}
}
return nullptr;
}
};
运行结果:
法二(双指针):
快慢指针法判断出有环之后,用数学的方法,我们先进行一些计算:
假设从head到入环位置有a个节点,slow和fast相遇的位置是第a+x个节点(即入环后第x个节点)。即慢指针走了(a+x)个节点,则快指针走了(2a+2x)个节点。快指针比慢指针多走的(a+x)个节点是环内节点数的整数倍(快指针多走了整数圈)。因此快指针从相遇位置再移动a个节点,就回到了入环位置。同时,让慢指针回到head点,和快指针同时以每次一个节点的速度移动,快慢指针将同时到达入环节点处。因此我们可以利用条件(slow==fast)判断到达的入环节点位置,并直接返回停在入环节点处的指针slow(或fast)。
代码实现(c++):
class Solution
{
public:
ListNode *detectCycle(ListNode *head)
{
if(head==nullptr) return nullptr;
ListNode* slow=head;
ListNode* fast=head;
while(fast->next!=nullptr&&fast->next->next!=nullptr)
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)
{
slow=head;
while(fast!=slow)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
}
return nullptr;
}
};
运行结果: