题目描述
一个链表中包含环,请找出该链表的环的入口结点。
方法一:
对链表进行遍历,利用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; } };