题目链接
思路:
采用双指针
准备一个快指针fast,一个慢指针slow,快指针一次走两步,慢指针一次走一步
初始时,快指针fast位于头节点的下一个节点,慢指针slow位于头节点
fast节点和slow节点同时开始向后走,若存在环,则一定会有一个时刻fast和slow指向同一个节点。若不存在,则最终fast走到null或者fast.next为null
若存在环,此时,slow和fast都位于环上
开始寻找第一个入环点:
寻找入环点的思路是:环的长度 + 未形成环的长度 = 链表总长度,第一个入环节点一定是未形成环路的最后一个节点的下一个节点,当拿到环的长度之后,先让一个节点从头走一个环的长度,然后另一个节点也从头开始,两个节点一起走,每次走一步,当相遇时,即为第一个入环点
这是因为此时这两个节点恰好走的长度为未形成环的长度,对于先走的那个节点来说,它走的总长度恰好为整个链表的长度,而后面走的节点,与先走的节点相遇时,走过的长度恰好是未形成环的长度,故相遇时即为第一个入环点。
例:
先判断是否有环路,s和f分别代表slow和fast,slow一次一步,fast一次两步,最终会在节点6处相遇
s f
1->2->3->4 1->2->3->4 ? ? -> ... -> ? ? 7<-6<-5 7<-6<-5s,f
寻找入环点。f先走,一次一步,计算环的长度=6,f从头开始走6步,达到节点7。s从头开始,和f一起走,会在节点2相遇,节点2就是第一个入环节点
s s,f
1->2->3->4 1->2->3->4 1->2->3->4 ? ? -> ? ? -> ? ?7<-6<-5 7<-6<-5 7<-6<-5f f
代码:
public ListNode detectCycle(ListNode head) {if (head == null || head.next == null) {return null;}// 快指针ListNode fast = head.next;// 慢指针ListNode slow = head;// 查看是否有环while (fast != null && fast.next != null) {if (slow == fast) {break;}slow = slow.next;fast = fast.next.next;}if (fast == null || fast.next == null) {return null;}int length = 1;fast = fast.next;// 计算环的长度while (fast != slow) {fast = fast.next;length++;}// fast从头开始往后走一个环的长度fast = head;while (length-- > 0) {fast = fast.next;}// slow从头开始,fast和slow同时一次一步,最终在入环点相遇slow = head;while (slow != fast) {slow = slow.next;fast = fast.next;}return slow;}class ListNode {int val;ListNode next;ListNode(int x) {val = x;next = null;}}