线性表(链表-快慢指针)
快慢指针指的是定义两个指针,这两个指针的移动速度一快一慢,以此来制造出自己想要的差值,通过这个差值找到链表上对应的结点。一般情况下,快指针的移动步长为慢指针的两倍。
1、中间值问题
a~g是链表的结点,想要通过快慢指针找到中间值,图示如下,slow指针每次移动一个步长,fast每次移动两倍slow指针的步长距离。
代码演示
public class FastAndSlowTest01 {
//结点类,这里是静态内部类
private static class Node<T>{
T item; //存储数据
Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
public static void main(String[] args) {
//创建结点
Node first = new Node("a",null);
Node second = new Node("b",null);
Node third = new Node("c",null);
Node fourth = new Node("d",null);
Node fifth = new Node("e",null);
Node sixth = new Node("f",null);
Node seven = new Node("g",null);
// Node eight = new Node("h",null);
// Node nine = new Node("i",null);
//指向
first.next = second;
second.next = third;
third.next = fourth;
fourth.next = fifth;
fifth.next = sixth;
sixth.next = seven;
// seven.next = eight;
// eight.next = nine;
//查找中间值
Object mid = getMid(first);
System.out.println(mid);
}
public static Object getMid(Node first){
//定义两个指针
Node fast = first;
Node slow = first;
//使用两个指针遍历链表,当快指针指向的结点没有下一个结点,则结束,结束之后,慢指针所指即为中间值
while(fast != null && fast.next != null){
//改变两个指针的值:
//这里的规律是:结合图来看哈,可以发现,fast的下一个位置是fast当前位置往前移了两次,同理slow,往前移了1次
fast = fast.next.next;
slow = slow.next;
}
return slow.item;
}
}
中间值结果为
d
无论是偶数个结点还是奇数个结点都是可以找出中间值的
当结点个数为8个的时候,中间值结果为e,结点个数为9个的时候中间值结果也为e
2、单链表是否有环问题
思路
通过使用快慢指针是否会相遇,来判断单链表是否有环。
关于为什么会相遇,会牵扯到数学知识,待我刷LeetCode遇到相关题后再详细写下来,哈哈哈(懒)
代码
public class FastAndSlowTest02 {
//结点类,这里是静态内部类,通过静态内部类可以创建结点
private static class Node<T>{
T item; //存储数据
Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
public static void main(String[] args) {
//创建结点
Node first = new Node("a",null);
Node second = new Node("b",null);
Node third = new Node("c",null);
Node fourth = new Node("d",null);
Node fifth = new Node("e",null);
Node sixth = new Node("f",null);
Node seven = new Node("g",null);
//指向
first.next = second;
second.next = third;
third.next = fourth;
fourth.next = fifth;
fifth.next = sixth;
sixth.next = seven;
//产生环
seven.next = third;
//判断是否有环
boolean circle = isCircle(first);
System.out.println(circle);
}
private static boolean isCircle(Node first) {
//定义两个指针
Node fast = first;
Node slow = first;
//遍历链表,如果两个指针同时指向了同一个结点,则有环
while(fast != null && fast.next != null){
//变换fast和slow指针
fast = fast.next.next;
slow = slow.next;
if(fast.equals(slow)){
return true;
}
}
return false;
}
}
3、有环链表的入口问题
就是找到成环的那个结点
图示
思路
先判断是否有环,判断了有环后,设定一个新指针指向链表的起点,且步长和慢指针一致为1,通过判断慢指针与新指针相遇的地方就是环的入口(这也牵扯到数学知识。。。懒,就不详细讲了,可以试试找规律)
代码
public class FastAndSlowTest03 {
//结点类,这里是静态内部类,通过静态内部类可以创建结点
private static class Node<T>{
T item; //存储数据
Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
public static void main(String[] args) {
//创建结点
Node first = new Node("a",null);
Node second = new Node("b",null);
Node third = new Node("c",null);
Node fourth = new Node("d",null);
Node fifth = new Node("e",null);
Node sixth = new Node("f",null);
Node seven = new Node("g",null);
//指向
first.next = second;
second.next = third;
third.next = fourth;
fourth.next = fifth;
fifth.next = sixth;
sixth.next = seven;
//产生环
seven.next = third;
Node entrance = getEntrance(first);
System.out.println(entrance.item);
}
private static Node getEntrance(Node first) {
//创建快慢指针
Node fast = first;
Node slow = first;
Node 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)){
break;
}
}
}
return temp;
}
}