原题是这个:(以下都是通过C来实现的并且是单向链表)
首先得需要知道是否有环形链表,判断:
一.环形链表判断:
首先,我们要知道如果不是环行链表,直线链表的尾节点肯定指向NULL,所以只要证明尾节点没有指向NULL即可;
我们可以把环形链表想象成这个图:
一个简单的数学问题:
一个男孩和一个女孩沿着一个圆圈走,男孩每次走两步,女孩每次走一步,那无论在圆的任何地方男孩总会追上女孩,因为圈的周长是固定的,而男孩与女孩的距离会一步一步缩减直到0。
故得出其相遇的节点即可证明是一个圆(毕竟同一起跑线跑,如果是直线男孩会越来越远),放到这题即可证明是一个环形链表。
这样我们把男孩设成指针quick,女孩为slow,放置在同一起跑线
quick走两步,slow走一步,假设其meet处会相遇,这样既求出相遇点和证明其是环形链表。代码如下:
struct ListNode
{
int val;
struct ListNode *next;
};
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode* quick = head;
struct ListNode* slow = head;
while(quick && quick->next)
{
quick = quick->next->next;
slow = slow->next;
if(quick == slow)
{
return quick;
}
}
二.找到进入环形的节点:
再来一个简单的数学问题:
设起点到入环节点距离为:L,入环节点到quick与slow相遇点meet距离为X,圆的周长为C。
由此我们可以得到这样的关系:
quick走的路程为:L+X+nC (n >= 1) n为在圈里面转的圈数。
slow走的路程为: L+X
而quick走的路程是slow走的两倍,
故得出:L+X+nC = 2(L+X)
化简为: L+X=nC
L=nC-X
通过这个式子我们得出:quick(slow)从起点出发,slow(quick)从相遇点(meet)出发,会在环形节点相遇。
不懂再解释一下:
假设:quick对应的L,slow对应的nC-X。quick已经走了L步,slow走了n圈还差X,就是nC-X,那就是相遇点:(如图)
meet-x不就是节点吗,不就是nC。
原理知道了我们来看题并实现代码:
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode* quick = head;
struct ListNode* slow = head;
struct ListNode* meet = NULL;
while(quick && quick->next)
{
quick = quick->next->next;
slow = slow->next;
if(quick == slow)
{
meet = quick;
break;
}
}
if(meet == NULL)
{
return NULL;
}
else
{
quick = head;
while(quick != slow)
{
quick = quick->next;
slow = slow->next;
}
return quick;
}
}
如果有错误请大佬指点