题目描述:
Given a linked list, return the node where the cycle begins. If there is no cycle, return
null
.Follow up:
Can you solve it without using extra space?
找到一个链表中循环的起始点,乍一看,感觉这道题目应该没有那么难的,可是做起来真心不简单,来分享一下我的做题目的纠结历程吧。高手就不要喷了……
第一次主要利用环上的节点和环外的节点的差别,找出环上的一个点,从环外遍历,看是否与环上的点node->next相等,若相等,则表示这个点就是所要找的点。代码:
ListNode *detectCycle(ListNode *head) { if(head==NULL) return NULL; ListNode*pre=head,*next=head->next; //找出环中的一个节点 while(next){ if(pre==next){ break; } next=next->next; if(next&&pre==next){ break; } if(next) next=next->next; pre=pre->next; } if(!next) return NULL; ListNode*temp=head; if(temp==pre) return head; next=next->next; while(temp!=pre){ while(temp!=next->next&&next!=pre){ next=next->next; } if(temp==next->next) return temp; temp=temp->next; } }
哎,折腾了好久,报错不断啊,难道我的思路错误了吗?换思路,环上最后一个点的next指针指向环开始点,同时这也是孩子节点在父节点之前的点:
bool find(ListNode *head, ListNode *testpNode) { ListNode*temp=head; while(temp!=testpNode->next){ if(temp==testpNode)//关键点在这里 return false; temp=temp->next; } return true; } ListNode *detectCycle(ListNode *head) { if(head == NULL) return NULL; ListNode *cur = head; while(cur != NULL) { if(find(head, cur)) return cur->next; cur = cur->next; } return NULL; }
我去,时间复杂度是O(n2),OJ给了一个Time Limit Exceeded。伤心我也。
最后参照这大牛http://blog.csdn.net/kenden23/article/details/13871699这个博客的解法,根据快慢指针,得到快指针走过的路程和慢指针走过的路程还有头结点到环首节点的距离之间的关系。需要注意的是,此处快慢指针要求比较严格,因为要用到快慢指针走的过程中的路径长度的关系,所以设初值的时候一定要注意slow=head->next,fast=slow->next要保证。当检测到循环之后,快慢指针重合,此时快慢指针的位置到环首节点的距离和链首节点到环首的距离是相等的。
为什么相等呢,设链首到环首距离是a,环首到快慢指针重合位置距离是b,重合位置到环首距离是c,又因为快指针走过的距离是慢指针走过距离的两倍,则可以得到2(a+b)=a+b+c+b,则a=c或者a特别长,那么指针就饶了很多圈,则最后的结论也是成立的。
ListNode *detectCycle(ListNode *head) { if(head == NULL||head->next==NULL) return NULL; ListNode*slow=head->next,*fast=head->next->next; while(fast){ if(slow==fast) break; slow=slow->next; fast=fast->next; if(fast) fast=fast->next; } if(!fast) return NULL; fast=head; while(fast!=slow){ fast=fast->next; slow=slow->next; } return slow; }
区区几行代码搞定了,这让我折腾了好久的菜鸟情何以堪啊。