首先肯定是要证明链表中有环存在,没环自然没有入口节点。而证明有环可以用快慢双指针的思想,即快指针每次走两步,慢指针每次走一步,如果有环,那么两指针一定会相遇,如果没有环,那么快指针直到抵达尾节点都不会和慢指针相遇。
接下来就该思考如何找到入口节点。如果知道了环中节点的数目为n,那么我们就可以使用前后双指针,让一个指针先走n步,随后让另一个指针从链头出发,两指针以相同速度移动,那么两指针相遇时的位置就是入口节点(因为前指针先走了n步,相当于绕环一圈,所以两指针相遇的位置就是入口节点),当然前指针先走n的整数倍步也是一样的。
下面就该解决如何得知环中节点数目的问题。当快慢指针相遇时,快指针比慢指针在环中多转了若干圈,假设快指针走了2k步,那么慢指针就走了k步,这说明快指针比慢指针多走的k步一定是环中节点数目的整数倍,那么将其中一个指针当作前指针,另设一指针从链头出发,两者相遇的地方就是入口节点。
public ListNode detectCycle(ListNode head) {
// 初始化快慢指针,都指向链表头
ListNode fast = head;
ListNode slow = head;
// 使用循环检测链表中是否存在环
while (true) {
// 如果 fast 或者 fast.next 为 null,说明链表中不存在环,返回 null
if (fast == null || fast.next == null) {
return null;
}
// 快指针走两步,慢指针走一步
fast = fast.next.next;
slow = slow.next;
// 如果快慢指针相遇,说明链表中存在环,跳出循环
if (slow == fast) {
break;
}
}
// 将快指针重新指向链表头当作后指针
fast = head;
// 使用循环找到环的起始节点
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
// 返回环的起始节点
return fast;
}