一、链表判环
如何判断一个简单的单向链表是否有环,我们只需要维护两个指针pSlow,pFast,一开始初始化为链表头指针,pSlow每步前进一个节点,pFast每步前进两个节点。如果链表有环,则pSlow和pFast肯定会相遇。
二、求环的起点与环长
求环的长度简单,因为我们已经知道两个指针的相遇地点一定是在环内,那么通过pSlow走了环一圈后,其实pFast已经走了两圈,且这两个指针会再次相遇。测试pSlow走了步长为环的长度。
求环的起点有点复杂,首先我们先把问题抽象为数学问题。我们设链表头结点pHead与环的起点距离为L个节点(不包括环的头结点,但是包含链表头结点,以下谈及的距离都是如此),环的头结点离节点P(pSlow与pFast相遇的节点)距离为S个节点,节点P离环的头结点距离为Y个点,我们假设pSlow走了T个时间单位,则pFast走了2T;环的长度为C;
则有这样几个方程:
2T = T + N1C ——-> T = N1C ®1
2T = L + S + N2C ——-> (2N1 – N2)C = L + S ®2
N3 = 2*N1 – N2 ——-> N3C = L + S ®3
C = S + Y ——-> (N3 – 1)C + Y = L ®4
由®4知,当pResult从pHead节点走到P节点时(即走了L距离),pFast从P节点同时出发,一定会在环的起点与pResult相遇,因为N3已知,整个方程一定有解的。记住此时,两个指针每步走一个节点。
struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode *pSlow = head, *pFast = head; bool flag = false; while (pFast != nullptr && pFast->next != nullptr) { pFast = pFast->next->next; pSlow = pSlow->next; if (pFast == pSlow) { flag = true; break; } } ListNode *Result = head; if (flag) { while (Result != pFast) { Result = Result->next; pFast = pFast->next; } } return flag == true ? Result : nullptr; } };