这道题非常地有趣,旨在找到链表中环的起始点。
哈希法
最容易的办法是遍历链表中的每个节点,用哈希表储存地址,如果有重复的内容,说明找到了环的起始点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public: ListNode *detectCycle(ListNode *head) { unordered_set<ListNode *> visited; while(head != nullptr) { if(visited.count(head)) { return head; } visited.insert(head); head = head->next; } return nullptr; } };
|
数学法
第二种办法是数学法,具体可以参考题解:环形链表
II(双指针法,清晰图解)
概括一下:
根据:
f = 2s (快指针每次2步,路程刚好2倍)
f = s + nb (相遇时,刚好多走了n圈)
推出:s = nb
从head结点走到入环点需要走 : a + nb,
而slow已经走了nb,那么slow再走a步就是入环点了。
如何知道slow刚好走了a步?
从head开始,和slow指针一起走,相遇时刚好就是a步.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode* fast = head; ListNode* slow = head; while (true) { if (fast == NULL || fast->next == NULL) return NULL; fast = fast->next->next; slow = slow->next; if (fast == slow) break; } fast = head; while (slow != fast) { slow = slow->next; fast = fast->next; } return fast; } };
|
巧妙法
O(n)算法,应该是最快的。
堆的地址从低到高,LeetCode的链表内存是顺序申请的,如果有环,head->next一定小于或等于head。
这个办法在实际生产中没法使用,因为堆内会不断地new和delete新的节点。
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public: ListNode *detectCycle(ListNode *head) { while(head) { if(!less<ListNode *>()(head, head->next)) { return head->next; } head = head->next; } return nullptr; } };
|