题目描述
一个链表中包含环,请找出该链表的环的入口结点。
方法一:
对链表进行遍历,利用set等结构,将已经遍历过的节点存入set,每遍历一个节点,判断set中是否已经存在该节点,如果存在,那么说明链表存在环,且该节点就是入口节点,返回该节点;如果遍历到NULL,说明已经到了链表结尾,说明该链表不存在环,返回NULL。代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if( pHead == NULL || pHead->next == NULL )
return NULL;
unordered_set<ListNode*> s;
ListNode* p = pHead;
while( p )
{
if( s.find(p) != s.end() )
return p;
s.insert(p);
p = p->next;
}
return NULL;
}
};
方法二:
分为两步:判断是否存在环,找到环的入口节点
1、判断是否存在环:利用两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步,如果两个指针相遇,则存在环;如果两个指针中任意一个等于NULL,说明不存在环。可以将相遇时的指针返回,后面会用到。
2、找环的入口:
首先判断环中节点个数,利用前面找到的相遇时的节点,该节点一定是在环中的,可以从该节点出发,一边向前一边计数,再次回到该节点时,就可以得到环中节点个数n了。
建立两个指针p1和p2,令p1先走n不,然后p1和p2以相同速度向前移动,直到他们相遇,他们相遇的节点就是环的入口,如下图所示。
代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if( pHead == NULL || pHead->next == NULL )
return NULL;
ListNode* meetingNode = MeetingNode(pHead);
if( meetingNode == NULL )
return NULL;
ListNode* p = meetingNode->next;
int count = 0;
while( p != meetingNode )
{
count++;
p = p->next;
}
p = pHead;
for( int i=0;i<=count;i++ )
p = p->next;
ListNode* p2 = pHead;
while( p != p2 )
{
p = p->next;
p2 = p2->next;
}
return p;
}
ListNode* MeetingNode(ListNode* pHead)
{
ListNode* p1 = pHead->next;
ListNode* p2 = pHead->next->next;
while( p1 != NULL && p2 != NULL )
{
if( p1 == p2 )
return p1;
p1 = p1->next;
p2 = p2->next;
if( p2 != NULL )
p2 = p2->next;
else
return NULL;
}
return NULL;
}
};