题目
对如下链表操作,检测是否有环,求入口节点,求环长,求链表长度
个人感觉求入口节点是最难的
一、检测是否有环
实现方法
采用快慢指针的方式检测链表是否有环,当快指针和满指针相遇(即相等)时代表有环
public static boolean isLoop(Node node){
Node fast=node;
Node slow=node;
while (fast.next!=null && fast.next.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
return true;
}
}
return false;
}
分析快慢指针变化
二、求入口节点
分析
假设从起点到入环点长度是D,入环点到首次相遇点长度是S1,首次相遇点到入环点是S2。S1+S2为整个环。快指针走俩步,慢指针走一步。
第一次相遇时,慢指针走的为D+S1,快指针走的为D+S1+n(S1+S2)
由于快指针走的是慢指针的2倍,所以D+S1+n(S1+S2)=2(D+S1),然后进行转化,转化过程中尽量将S1和S2合在一起,因为S1+S2是环长,快指针一定多走n圈S1+S2,n最少为1,所以可以尽量配出n-1。
演算过程:
D+S1+n(S1+S2)=2(D+S1)
n(S1+S2)=D+S1
D=n(S1+S2)+S1
D=(n-1)S1+nS2
D=(n-1)S1+(n-1)S2+S2
D=(n-1)(S1+S2)+S2
假设快指针多走了1圈环相遇,即n为1,则D=S2
所有通过从相遇点走到入环点的长度和从起点走到入环点的长度相等这个条件就可以求出入环点。
实现方法
public static Node entryNodeOfLoop(Node node){
Node fast=node;
Node slow=node;
while (fast.next!=null && fast.next.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
break;
}
}
//头节点
Node head=node;
//相遇节点
Node meet=slow;
//头结点和相遇节点相等时(有点复杂)
while (head!=meet){
head=head.next;
meet=meet.next;
}
return head;
}
三、求环长
分析
关于环长,从相遇节点让慢指针再走一圈就是环的长度了
实现方法
public static int nodeNumOfLoop(Node node){
Node fast=node;
Node slow=node;
while (fast.next!=null && fast.next.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
break;
}
}
//计算环节点个数
int count=0;
//相遇节点
Node temp=slow;
do{
slow=slow.next;
count++;
}while (slow!=temp);
return count;
}
四、求链表长度
分析
链表长度就是环长加上从起点到入口节点的长度
实现方法
public static int nodeLength(Node node){
Node slow=node;
//入口节点
Node entryNode = entryNodeOfLoop(node);
//环长
int LoopLength = nodeNumOfLoop(node);
int length=0;
while (entryNode!=slow){
slow=slow.next;
length++;
}
//链表长度
int nodeLength=length+LoopLength;
return nodeLength;
}
源码
public class LinkedListCircle {
public static class Node{
private int value;
private Node next;
public Node(int value,Node next){
this.value=value;
this.next=next;
}
}
/**
* 判断链表是否有环 有环为true 无环为false
*
* @param node
* @return boolean
* @author 崔晓鸿
* @since 2019/12/25 10:57
*/
public static boolean isLoop(Node node){
Node fast=node;
Node slow=node;
while (fast.next!=null && fast.next.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
return true;
}
}
return false;
}
/**
* 求环的入口节点
*
* @param node
* @return main.java.linkedList.LinkedListCircle.Node
* @author 崔晓鸿
* @since 2020/1/2 15:18
*/
public static Node entryNodeOfLoop(Node node){
Node fast=node;
Node slow=node;
while (fast.next!=null && fast.next.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
break;
}
}
//头节点
Node head=node;
//相遇节点
Node meet=slow;
//头结点和相遇节点相等时(有点复杂)
while (head!=meet){
head=head.next;
meet=meet.next;
}
return head;
}
/**
* 环内节点个数
*
* @param node
* @return int
* @author 崔晓鸿
* @since 2020/1/2 15:48
*/
public static int nodeNumOfLoop(Node node){
Node fast=node;
Node slow=node;
while (fast.next!=null && fast.next.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast){
break;
}
}
//计算环节点个数
int count=0;
//相遇节点
Node temp=slow;
do{
slow=slow.next;
count++;
}while (slow!=temp);
return count;
}
/**
* 求链表长度
*
* @param node
* @return int
* @author 崔晓鸿
* @since 2020/1/2 15:59
*/
public static int nodeLength(Node node){
Node slow=node;
//入口节点
Node entryNode = entryNodeOfLoop(node);
//环长
int LoopLength = nodeNumOfLoop(node);
int length=0;
while (entryNode!=slow){
slow=slow.next;
length++;
}
//链表长度
int nodeLength=length+LoopLength;
return nodeLength;
}
public static void main(String[] args) {
Node node5=new Node(5,null);
Node node4=new Node(4,node5);
Node node3=new Node(3,node4);
Node node2=new Node(2,node3);
Node node1=new Node(1,node2);
node5.next=node3;
//是否有环
boolean loop = isLoop(node1);
System.out.println(loop);
//入口节点
Node entryNode = entryNodeOfLoop(node1);
System.out.println(entryNode);
//环的节点个数
int nodeNum = nodeNumOfLoop(node1);
System.out.println(nodeNum);
//求链表长度
int nodeLength = nodeLength(node1);
System.out.println(nodeLength);
}
}
运行结果
我这里放的是关于链表是否有环的运行结果的快慢指针变化。
初始值
第1次循环
第2次循环
第3次循环