import java.util.*; /** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) { //使用两个指针指向入环节点和出环节点 if (head == null) return false; ListNode inNode,outNode; inNode = head; outNode = inNode.next; while (outNode != null){ if (outNode.next == null) return false; if (outNode.next == inNode) return true; //System.out.println(outNode.val); //System.out.println(inNode.val); inNode = inNode.next; outNode = outNode.next; if (outNode.next == null) return false; if (outNode.next == inNode) return true; outNode = outNode.next; } return false; } }
这里使用的是双指针,一个快指针一个慢指针,快指针每次移动两个位置,慢指针每次移动一个位置,如果有环,快慢指针会重合。使用这样的一对快慢指针,每一次循环,快指针和慢指针之间的距离都会+1。当快慢指针重合时,说明环的长度等于快慢指针之间距离的约数。
为什么有环情况下 快慢 指针会重合?这个问题应该可以转换成一个数学问题。
假设快指针在链表中下标为fastIndex,慢指针在链表中下标为slowIndex:
初始都为0,但fastIndex每次+2,slowIndex每次+1,所以fastIndex = 2 * slowIndex;
有环情况下,假设环长度为n,链表中,环前节点个数为preNum,当循环 i 次时,
快指针移动了 2i, 而fastIndex = (2i-preNum)% n + preNum;
慢指针移动了 i,slowIndex = (i-preNum)% n +preNum。
有环情况下,快慢指针会重合也就意味着 存在i 使得fastIndex = slowIndex,
即(2i-preNum)% n = (i-preNum) % n
那么这样的i存在吗?
2i-preNum = tkn+b
i-preNum = kn+b
t,k是整数,b是整数且0<=b<n。消去 i 可以得到;
(t-2)kn = b+preNum;因为b+preNum>=0,所以t>=2,说明快指针至少比慢指针快一圈。
(t-2)k为整数,且>=0,而总存在b使得b+preNum为n的整数倍,
比如preNum等于0时,只需b=0,则满足b+preNum是n的0倍,这说明当链表是一个环时,两个指针会在环的起始节点汇合。
当preNum>0时,总存在整数b,0<b<n使得b+preNum是n 的整数倍,具体是多少倍需要看preNum的大小。