单链表-快慢指针
快慢指针指的是定义两个指针,这两个指针的移动速度一快一慢,通常快指针的移动速度为慢指针的2倍。以此来制造出自己想要的差值,这个差值可以帮我们找到链表上相应的结点。
1. 中间值问题
原理:利用快慢指针,我们可将一个链表看成是一个跑道,假设a的速度是b的两倍,那么当a跑完全程后,b刚好跑完一半,以此来找到中间结点。
/**
* 快慢指针求链表中间值
* 当链表结点为奇数个:如7个结点,那么中间为(7+1)/2 = 4
* 当链表结点为偶数个:如6个结点,那么中间为(6+2)/2 = 4
*/
public class FastSlowPointer {
public static void main(String[] args) {
//声明结点
Node<String> first = new Node<>("aa", null);
Node<String> two = new Node<>("bb", null);
Node<String> three = new Node<>("cc", null);
Node<String> four = new Node<>("dd", null);
Node<String> five = new Node<>("ee", null);
Node<String> six = new Node<>("ff", null);
Node<String> seven = new Node<>("gg", null);
//结点链接
first.next = two;
two.next = three;
three.next = four;
four.next = five;
five.next = six;
six.next = seven;
String mid = getMid(first);
System.out.println(mid);
}
/**
* 利用快慢指针查找链表中间值。
* 一般快指针的跳转是慢指针的二倍,快指针到最后结点时候,慢指针正好指着链表中间结点
* 1. 定义快、慢两个指针,皆指向第一个结点
* 2. 进入while循环,将快指针运行速度设定为慢指针的2倍
* @param first :链表第一个结点
* @return :返回中间结点的数据域
*/
private static String getMid(Node<String> first) {
Node<String> slow = first;
Node<String> fast = first;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow.item;
}
//定义结点
private static class Node<T> {
//结点数据
private T item;
//下一个结点
private Node<T> next;
public Node(T item, Node<T> next) {
this.item = item;
this.next = next;
}
}
}
2. 判断单链表是否有环
原理:使用快慢指针的思想 ,将链表比作一条跑道,那么这条跑道就是一条圆环跑道,在一条圆环跑道中 ,两个人有速度差,那么终有快慢指针相遇的时刻,只要相遇就表示有环。
/**
* 快慢指针判断是否链表有环
*/
public class FastSlowPointerIsCircle {
public static void main(String[] args) {
Node<String> first = new Node<>("aa", null);
Node<String> two = new Node<>("bb", null);
Node<String> three = new Node<>("cc", null);
Node<String> four = new Node<>("dd", null);
Node<String> five = new Node<>("ee", null);
Node<String> six = new Node<>("ff", null);
Node<String> seven = new Node<>("gg", null);
//结点链接
first.next = two;
two.next = three;
three.next = four;
four.next = five;
five.next = six;
six.next = seven;
//产生环
seven.next = three;
System.out.println(isCircle(first));
}
/**
* 判断是否存在环
* 方法:因为快指针比慢指针快二倍,如果存在环,则二者肯定会再某一刻指向相同
* @param first
* @return
*/
private static boolean isCircle(Node<String> first){
//快指针
Node<String> fast = first;
//慢指针
Node<String> slow = first;
while (fast!=null&&fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if (fast.equals(slow)){
return true;
}
}
return false;
}
//定义结点
private static class Node<T> {
//结点数据
private T item;
//下一个结点
private Node<T> next;
public Node(T item, Node<T> next) {
this.item = item;
this.next = next;
}
}
}
3. 求有环链表入口问题
原理:快慢指针相遇,则证明链表有环,这时重新设定一个新指针指向链表的起点,且步长与慢指针相同都为1,当慢指针与新指针相遇的地方,就是环的入口。
public class FastSlowPointerIsCircle {
public static void main(String[] args) {
Node<String> first = new Node<>("aa", null);
Node<String> two = new Node<>("bb", null);
Node<String> three = new Node<>("cc", null);
Node<String> four = new Node<>("dd", null);
Node<String> five = new Node<>("ee", null);
Node<String> six = new Node<>("ff", null);
Node<String> seven = new Node<>("gg", null);
//结点链接
first.next = two;
two.next = three;
three.next = four;
four.next = five;
five.next = six;
six.next = seven;
//产生环
seven.next = three;
System.out.println(getEntrance(first).item);
}
/**
* 获得环的入口
* 当快慢指针相遇时,表示表中有环,这是重新设定一个新的指针指向链表起点,且步长与slow相同为1
* 当慢指针与新指针相遇的地方就是环的入口。
* @param first
* @return
*/
private static Node<String> getEntrance(Node<String> first){
//快指针
Node<String> fast = first;
//慢指针
Node<String> slow = first;
//临时指针·新节点
Node<String> temp = null;
while (fast!=null&&fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if (fast.equals(slow)){
temp = first;
continue;
}
if (temp!=null){
temp = temp.next;
if (temp.equals(slow)){
return temp;
}
}
}
return null;
}
//定义结点
private static class Node<T> {
//结点数据
private T item;
//下一个结点
private Node<T> next;
public Node(T item, Node<T> next) {
this.item = item;
this.next = next;
}
}
}