2021/04/22
前言
如下链表是一个带环链表,环的入口是 2,环的长度为 4。
链表节点定义如下:
// 单链表节点定义,参考 Leetcode
class ListNode<T> {
public T val;
public ListNode<T> next;
public ListNode(){
this(null,null);
}
public ListNode(T val){
this(val,null);
}
public ListNode(T val, ListNode next){
this.val = val;
this.next = next;
}
}
判断链表是否有环
判断链表是否有环,比较好的方法是 快慢指针法。
定义两个指针 fast, slow ,初始都指向头节点。快指针 fast 一次向后移动一位,慢指针 slow 一次向后移动两位;如果快慢指针相遇(指向同一个节点且都不为空),说明链表存在环。
// 判断链表是否有环
public boolean isLoop(){
if(this.head == null){
return false;
}
ListNode<T> fast = head;
ListNode<T> slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true;
}
}
return false;
}
确定环入口
如果链表存在环,如何确定环的入口(环交汇的节点)是哪个节点?
假设链表环的入口为 entrance ,快慢指针相遇的节点为 meet,由于快指针的速度是慢指针的 2 倍,所以当快慢指针相遇时,慢指针走过的距离为 s,则快指针走过的距离为 2s。
假设头节点到环入口的距离为 he,环入口到相遇节点的距离为 em,环的长度为 r。
我们可以得到:
其中,n 表示快指针走过的环的圈数。所以 ,即相遇时,慢指针走过的距离是环长度的整数倍。
假设链表的长度为 L,有 L = he + r,则
得到
即 如果从相遇点和头节点以相同的速度向后移动,走经过 n-1 个环,能够在环入口相遇。
所以我们得到如何得到环入口的方法:
- 找到快慢指针相遇的节点
meet - 定义两个指针,分别指向
head和meet - 双指针同时向后移动,直到两个指针指向同一个节点,这个节点就是环的入口
public ListNode getLoopEntrance(){
if(this.head == null){
return null;
}
ListNode<T> meet = null;
ListNode<T> fast = head;
ListNode<T> slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
meet = fast;
break;
}
}
// meet == null 说明没有相遇即链表没有环
if(meet == null){
return null;
}
// 快慢指针从相遇点开始以相同的速度向后移动
fast = head;
slow = meet;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
确定环的长度
在一个环上,快慢指针速度相差两倍。从相遇节点开始,快慢指针依次按照各自的速度向后移动,慢指针移动一次长度增加 1 ,当快慢指针相遇时,慢指针走过的距离就是环的长度。
public int getLoopLength() {
if (head == null || head.next == null) {
return -1;
}
ListNode<T> fast = head.next;
ListNode<T> slow = head.next;
boolean isLoop = false;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
isLoop = true;
break; //此时fast和slow第一次相遇
}
}
// 如果链表没有环,返回 0
if(!isLoop){
return 0;
}
// 已经相遇,len 初始为 1 ,快慢指针继续向后移动
int len = 1;
slow = slow.next;
fast = fast.next.next;
while (slow != fast) {
slow = slow.next;
fast = fast.next.next;
len++;
}
return len;
}