题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
有下面三种方法:
1. 遍历链表,使用map保存每个节点是否出现过,第一个出现两次的的节点就是环的入口
2. 断链法,遍历每一个节点,使其next为NULL,则最后一个next为空的节点就是入口
3. 快慢指针法,慢指针每次走一步,快指针每次都两步,则两个必然在环内相遇,
相遇后快指针从head开始,每次走一步,慢指针在环内继续每次走一步,则相遇点为入口
证明如下,设链表开始到入口长度为x, 环长度为c, 环入口到第一次相遇点长度为a
则慢指针走的路程为 Sslow = x + c*m + a, 快指针走的路程为 Sfast = x + c*n + a,并且 2 * Sslow = Sfast
所以可以导出 x = (n - 2 * m )*c - a = (n - 2 *m -1 )*c + c - a
c - a 是从相遇点,到入口的长度
也就是说,如果再有两个新指针,一个从头开始走,另一个从第一次的相遇点继续走, 则必然在入口再次相遇
代码如下:
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
//方法一:快慢指针
ListNode* entrynode_2pointer(ListNode* head){
if(NULL == head || NULL == head->next){
return NULL;
}
ListNode* slow = head;
ListNode* fast = head;
while(NULL != fast && NULL != fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
fast = head;
while(slow != fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return NULL;
}
//方法二:断链法
ListNode* entrynode_breaklink(ListNode* head){
if(NULL == head || NULL == head->next){
return NULL;
}
ListNode* node = head;
while(node->next){
ListNode* temp = node->next;
node->next = NULL;
node = temp;
}
return node;
}
//方法三:使用map保存节点是否出现过,第一个出现两次的节点就是入口
ListNode* entrynode_map(ListNode* head){
if(NULL == head || NULL == head->next){
return NULL;
}
map<ListNode*, int> nodemap;
ListNode* node = head;
while(node){
if(nodemap[node] == 1){
return node;
}
else{
nodemap[node] = 1;
node = node->next;
}
}
return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead){
//turn entrynode_2pointer(pHead);
//return entrynode_breaklink(pHead);
return entrynode_map(pHead);
}
};