一、快慢指针解决单链表中间值问题
思路分析:
可以利用两个指针,分别从链表头部遍历链表,一个每次走一个节点(slow),一个每次走两个节点(fast)。这样,当链表节点有奇数个的时候,当快指针走到NULL前面一个节点的时候,此时,慢指针刚好指向链表的中间节点。例如(1,2,3,4,5),慢指针刚好在3这个位置上。当链表节点有偶数个的时候,当快指针走到NULL的时候,此时,慢指针刚好指向链表的中间节点。例如(1,2,3,4,5,6),慢指针刚好在4这个位置上。
代码展示如下:
/* 快慢指针解决链表中点问题 (一道leetCode算法题)*/
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head, slow = head;
//注意:必须要保证fast != null 并且 fast.next != null
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
二、快慢指针解决单链表有无环问题
思路分析:
设置两个指针,一快一慢(快的每次移动的步数是慢的二倍),在这个过程中,如果有环,快指针先进入环,慢指针后续也会进入环,当快慢指针相遇时,即slow == fast(判断条件) ,有环,其实就是快指针绕环转两周,慢指针则转一周,即快指针多走的距离等于环长。如果没有环,快慢指针就不可能相遇,快指针随后移动到末尾null,而慢指针移动到该链表的中间节点位置。
代码展示如下:
package linear;
/**
* 快慢指针--有无环问题
*/
public class CircleListCheckTest {
public static void main(String[] args) {
//创建结点
Node<String> first = new Node<>("aa", null);
Node<String> second = 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 = second;
second.next = three;
three.next = four;
four.next = five;
five.next = six;
six.next = seven;
//产生环
seven.next = three;
//判断链表是否有环
boolean circle = isCircle(first);
System.out.println("first链表中是否有环:" + circle);
}
/**
* @param first 链表首结点
* @return 链表有环 true 无环 false
*/
private static boolean isCircle(Node<String> first) {
//定义两个指针
Node<String> fast = first;
Node<String> slow = first;
//使用两个指针遍历链表,当快指针指向的结点没有下一个结点了,结束循环
//当快指针指向的和慢指针相遇时,则证明有环 否则无环
//核心代码块
while (fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast.equals(slow)) {
return true;
}
}
return false;
}
//定义结点类
private static class Node<T> {
//存储数据
T item;
//下一个结点
private Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
三、快慢指针解决单链表环入口问题
思路分析:
要解决环入口,就必须先判断有无环,若有环,则根据先前说到的快慢指针相遇的时候,再重新设立一个与慢指针同速率的一个临时指针(temp)指向头节点,然后当慢指针和临时指针相遇时,该点就是环入口。
代码展示如下:
package linear;
/**
* 快慢指针--环入口问题
*/
public class CircleListInTest {
public static void main(String[] args) {
//创建结点
Node<String> first = new Node<>("aa", null);
Node<String> second = 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 = second;
second.next = three;
three.next = four;
four.next = five;
five.next = six;
six.next = seven;
//产生环
seven.next = three;
//查找环的入口结点
Node entrance = getEntrance(first);
System.out.println("first链表的环入口结点元素为:" + entrance.item);
}
/**
* @param first 链表首结点
* @return 环入口结点元素
*/
private static Node getEntrance(Node<String> first) {
//定义两个指针,和临时指针
Node<String> fast = first;
Node<String> slow = first;
Node<String> temp = null;
//使用两个指针遍历链表,当快指针指向的结点没有下一个结点了,结束循环
//当快指针指向的和慢指针相遇时,则证明有环 否则无环
//核心代码块
while (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;
}
//定义结点类
private static class Node<T> {
//存储数据
T item;
//下一个结点
private Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}