这道题记忆犹新啊,校招期间,被两家公司都问了,深信服和百度!!!坑爹。。。
然后csdn上发现了http://blog.csdn.net/fty8788/article/details/6531280 本文大部分来自这里
题目很简单:如何检查一个单链表上是否有环?
首先,既然是单链表,如果有环,就肯定是在链表末尾位置。
1. 首先想到的办法当然是把地址保存起来,然后遍历,重复出现的时候就表示此链有环,保存可以用哈希表,时间复杂度是O(n)
2. 第二个方法比较巧妙,试用反转指针,每过一个节点都把该节点的指针反转。
Boolean reverse(Node *head) {
Node *curr = head;
Node *next = head->next;
curr->next = NULL;
while(next!=NULL) {
if(next == head) { /* go back to the head of the list, so there is a loop */
next->next = curr;
return TRUE;
}
Node *temp = curr;
curr = next;
next = next->next;
curr->next = temp;
}
/* at the end of list, so there is no loop, let's reverse the list back */
next = curr->next;
curr ->next = NULL;
while(next!=NULL) {
Node *temp = curr;
curr = next;
next = next->next;
curr->next = temp;
}
return FALSE;
}
好像代码不是那么好理解,不过自己在纸上跑一遍就会明白了。核心在:如果存在环最终链表遍历后会回到起点,如果不存在则不会,最后重新将反转后的链表反转回去
优点就是空间复杂度是O(1),只用了三个指针,时间复杂度为O(n),但是有个缺点:在多线程情况下会出现改变表的状态。
3. 第三个办法就是一般情况下的所谓的答案了,快慢指针,记得深信服的面试官曾想过把我往这方面指引,只怪我当时大脑一时没反应过来啊
int is_link_list_cicled(Node* head)
{
Node *p = head, *q = head;
while(p && q)
{
p = p-> next;
q = q-> next;
if(!q)
return 0;
q = q-> next;
if(p == q)
return 1;
}
return 0;
}
至于如何证明这样做的正确性,数学学的不错的应该很容易吧。。。
接下来,扩展一下,如何找到那个环的入口点:这个算法理解起来比较难,慢慢啃吧。。。
slist* FindLoopPort(slist *head)
{
slist *slow = head, *fast = head;
while ( fast && fast->next )
{
slow = slow->next;
fast = fast->next->next;
if ( slow == fast ) break;
}
if (fast == NULL || fast->next == NULL)
return NULL;
slow = head;
while (slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}