这道题记忆犹新啊,校招期间,被两家公司都问了,深信服和百度!!!坑爹。。。
然后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; }