题目描述
链表中环的入口节点是指一个有环链表中,环的入口节点。给定一个链表,若其中包含环,则输出环的入口节点;否则输出null。
例如,在如下图所示的链表中,环的入口节点是3(注意,这里环的入口节点不是算链表的第3个节点)。
说明:
不允许修改给定的链表。
要求空间复杂度为O(1)。
解题思路
链表中环的入口节点可以通过 Floyd 算法来解决,具体步骤如下:
- 使用快慢指针法判断链表是否有环,如果有,快指针回到链表头部,然后快慢指针以相同的速度向前移动,当两个指针再次相遇时,该节点就是环的入口节点。
- 如何证明这个算法是正确的?假设链表的头部到环的入口节点的距离为a,环的入口节点到两个指针相遇点的距离为b,相遇点到环的入口节点的距离为c。同时,假设快指针速度为慢指针的两倍,即v{fast}=2v{slow},慢指针走过的距离为x。
当快指针第一次到达相遇点时,快指针走过的距离为 a + nb + x,慢指针走过的距离为 a + x,由于快指针走过的距离是慢指针的两倍,因此有2(a+x)=a+nb+x,化简得到 a=(n-1)b+c。
当快指针回到链表头部时,慢指针距离环的入口节点的距离为a,因此快慢指针分别从链表头部和相遇点开始移动,相遇的节点即为环的入口节点。
因此,这种方法可以在O(1)的空间复杂度下找到链表中环的入口节点。
具体代码展示
package org.zyf.javabasic.letcode.list.application;
import org.zyf.javabasic.letcode.list.base.ListNode;
/**
* @author yanfengzhang
* @description
* 链表中环的入口节点是指一个有环链表中,环的入口节点。给定一个链表,若其中包含环,则输出环的入口节点;否则输出null。
* 说明:
* 不允许修改给定的链表。
* 要求空间复杂度为O(1)。
* @date 2023/4/5 23:45
*/
public class EnterNodeInCycleList {
/**
* 使用快慢指针法判断链表是否有环,
* 如果有,快指针回到链表头部,然后快慢指针以相同的速度向前移动,
* 当两个指针再次相遇时,该节点就是环的入口节点。
* <p>
* 该算法的时间复杂度为O(n),空间复杂度为O(1)。
*/
public ListNode entryNodeOfLoop(ListNode head) {
ListNode fast = head;
ListNode slow = head;
/*1. 判断是否有环*/
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
break;
}
}
if (fast == null || fast.next == null) {
/*说明没有环*/
return null;
}
/*2. 有环,找入口节点*/
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
head.next.next.next = new ListNode(4);
head.next.next.next.next = new ListNode(5);
head.next.next.next.next.next = new ListNode(6);
head.next.next.next.next.next.next = head.next.next;
ListNode entryNode = new EnterNodeInCycleList().entryNodeOfLoop(head);
System.out.println(entryNode.val);
}
}