思路
判断是否有环:定义快慢指针,慢指针(slow)走一步,快指针(fast)走两步。
- 无环:快指针最终会到达链表的末尾(即
fast.next
为null
),永远不可能相遇- 有环:当slow进入环中,fast开始在环中追赶slow,两指针每一次移动,对于slow来说,fast都在一步一步靠近slow。当slow走一圈,fast已经走完两圈(fast是slow速度的两倍),由于fast是逐步靠近slow,所以两指针必然在slow进环的第一圈内相遇,相遇即有环
寻找环的入口:链表head到入口点距离=两指针相遇点到入口点距离
- 当slow到环入口时,fast已经在环内走了N>=1圈,设slow到入口点距离X,入口点到相遇点距离Y,环中相遇点到入口点距离Z(顺时针),则相遇时两指针走的节点分别为:
- slow:X+Y
- fast:X+Y+N*(Y+Z)
- 步数:slow : fast = 1 : 2
- 则在相遇点可以推出公式:2(X+Y)=X+Y+N*(Y+Z)
- ==>(X+Y)=N*(Y+Z)
- ==> X=N*(Y+Z)-Y
- ==> X=(N-1)Y+N*Z 当N=1时,X=Z
环的性质:在链表中,如果存在环,那么从链表头部到环入口的距离等于环入口到快慢指针相遇点的距离。
题目
示例代码
var detectCycle = function (head) {// 如果头节点为空或者头节点的下一个节点为空,说明链表中没有环if (!head || !head.next) return null;// 初始化两个指针,slow和fast,都指向头节点let slow = head, fast = head;// 使用while循环进行快慢指针的移动,直到fast指针不能再移动while (fast && fast.next) {// 移动fast指针,一次走两步fast = fast.next.next;// 移动slow指针,一次走一步slow = slow.next;// 如果fast指针追上了slow指针,说明链表中有环if (fast === slow) {// 指针index1 指向头结点// 指针index2 指向相遇点let index1 = head, index2 = fast;// 使用while循环移动index1和index2,直到它们相遇// 这一步是为了找到环的起始节点while (index1 !== index2) {// 两指针没遇到就继续走index1 = index1.next;index2 = index2.next;}// 当index1和index2相遇时,返回index1,即环的起始节点return index1;}}// 如果没有检测到环,返回nullreturn null;
};
思路及图片借鉴:@代码随想录
欢迎指正!