[编程|20分] 链表中环的入口结点
时间限制:C/C++ 1秒,其他语言 2秒
空间限制:C/C++
32768K,其他语言 65536K
题目描述
一个链表中包含环,请找出该链表的环的入口结点。
思路:
已知链表带环,那么我们便不需要判断链表带环与否,而主要关注找到环入口的方法:
设置两个指针(pFaster, pSlower),初始值都指向头,pSlower每次前进一步,pFaster每次前进二步,
第一种方法:当Fpaster若与Slower相遇时,pSlower肯定没有走遍历完链表(这是因为如果这个链表整整体是个环的话,当pSlower恰好到达链表终点的时候pFaster正好追上pSlower,当这个环不是全部链表部分的时候,则肯定在pSlower到达终点的时候就被Faster追上了),而Faster已经在环内循环了n圈(n>=1)。假设pSlower走了s步,则Faster走了2s步(Faster步数还等于s加上在环上多转的n圈),设环长为r,则:
因为2s = s + nr
所以得出s= nr
设整个链表长L,环入口与相遇点距离为x,起点到环入口点的距离为a。
则有a + x = s 推出 a = nr
分解有a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
程序描述如下:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead){
ListNode *pSlower = pHead, *pFaster = pHead;
while ( pFaster && pFaster->next ){
pSlower = pSlower->next;
pFaster = pFaster->next->next;
if ( pSlower == pFaster )
break;
}
if (pFaster == NULL || pFaster->next == NULL)
return NULL;
pSlower = pHead;
while (pSlower != pFaster){
pSlower = pSlower->next;
pFaster = pFaster->next;
}
return pSlower;
}
};
还有一种方法就是使用一个指针,每次遍历一个的时候把这个节点的地址存到一个vector,下一个遍历的时候从vector中查找看是否存在,如果在指针为NULL之前,出现了在vector中的节点,则说明有环存在。
下面看如何从两个相交链表找出相交点,其实这是链表环的一个衍生问题
一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。
二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。
这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。
python实现
思路:
同样的两个指针一个Faster、一个Slower同时从一个链表的头部出发,Faster一次走2步,Slower一次走一步,如果该链表有环,两个指针必然在环内相遇,此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内),这次两个指针一次走一步,相遇的地方就是入口节点。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
pFaster = pHead
pSlower = pHead
while pFaster is not None and pFaster.next is not None:
pFaster = pFaster.next.next
pSlower = pSlower.next
if pFaster is pSlower:
break
if not pFaster or not pFaster.next:
return None
pFaster = pHead
while (pFaster != pSlower):
pFaster = pFaster.next
pSlower = pSlower.next
return pFaster