在处理链表的时候,我们经常用到两个指针遍历链表:previous 和 current,也就是current 比 previous 超前一个节点。而这篇博文我要介绍的“快行指针”指针技巧其实也是类似的。
两个指针,一个slowRunner,一个fastRunner;fastRunner 的“快”可以体现在它比 slowRunner 超前 N 个节点,也可以体现在它“跑得更快”,比如slowRunner每次前进一个节点,fastRunner 每次前进2个节点。具体的设计根据实际问题来。下面举几个例子,帮助大家更好地理解。
链表问题我们应该第一步思考就尝试能不能一次遍历解决问题,所以那些多次遍历的算法我就不介绍了。这里直接介绍“快行指针”的解法。
public MyListNode nth2End(MyListNode list,int k) {
// slowRunner 和 fastRunner 的初始化
MyListNode slowRunner = list;
MyListNode fastRunner = slowRunner;
for(int i=0; i<k;i++){
fastRunner = fastRunner.next;
if(fastRunner == null){
return null;
}
}
// 遍历开始
while(fastRunner != null){
fastRunner = fastRunner.next;
slowRunner = slowRunner.next;
}
return slowRunner;
}
package MyList;
import java.util.Random;
public class MyListTest {
public static void main(String[] args) {
MyListTest listTest = new MyListTest();
MyListNode list = listTest.buildList(15);
listTest.display(list);
int k = 6;
MyListNode node = listTest.nth2End(list, k);
System.out.printf("The %d-th node to the end is: %d",k,node.val);
}
public MyListNode nth2End(MyListNode list,int k) {
// slowRunner 和 fastRunner 的初始化
MyListNode slowRunner = list;
MyListNode fastRunner = slowRunner;
for(int i=0; i<k;i++){
fastRunner = fastRunner.next;
if(fastRunner == null){
return null;
}
}
// 遍历开始
while(fastRunner != null){
fastRunner = fastRunner.next;
slowRunner = slowRunner.next;
}
return slowRunner;
}
/** * 创建节点数为num的单项链表,并返回首节点 */
public MyListNode buildList(int num) {
if (num == 0) {
return null;
}
MyListNode header = new MyListNode();
Random random = new Random();
int val;
MyListNode p = header;
for (int i = 0; i < num; i++) {
val = random.nextInt(100);
p.next = new MyListNode(val);
p = p.next;
}
return header.next;
}
/** * 打印链表 * */
public void display(MyListNode list) {
System.out.println("***** Output the list: *****");
MyListNode p = list;
int count = 0;
while (p != null) {
System.out.printf("%-5d", p.val);
p = p.next;
count++;
if (count == 5) {
System.out.println();
count = 0;
}
}
}
}
***** Output the list: *****
89 65 1 1 63
67 11 90 13 31
59 80 49 22 52
The 6-th node to the end is: 31
算法:
public boolean hasLoop(MyListNode head) {
MyListNode slowRunner = head;
MyListNode fastRunner = slowRunner;
while (fastRunner != null) {
// slowRunner每次前进1步
slowRunner = slowRunner.next;
// fastRunner每次前进2步
// 每前进一步就进行判空,如果为空则说明到达链表的尾部,返回false
fastRunner = fastRunner.next;
if(fastRunner != null){
fastRunner = fastRunner.next;
}
else {
return false;
}
// 判断 slowRunner 与 fastRunner 是否相等
if(slowRunner.equals(fastRunner)){
return true;
}
}
return false;
}
***** Output the list: *****
58 80 78 6 38
94 52 11 94 68
28 86 56 23 87
The 6-th node to the end is: 68
false
true
首先,fastRunner比slowRunner快,如果没有环,他们是不可能相遇的,那么他们相遇的节点有什么特性呢?
public MyListNode getLoopBegin(MyListNode head) {
MyListNode slowRunner = head;
MyListNode fastRunner = slowRunner;
while (fastRunner != null) {
// slowRunner每次前进1步
slowRunner = slowRunner.next;
// fastRunner每次前进2步
// 每前进一步就进行判空,如果为空则说明到达链表的尾部,返回false
fastRunner = fastRunner.next;
if (fastRunner != null) {
fastRunner = fastRunner.next;
} else {
System.err.println("The list doesn't have loop!");
return null;
}
// 判断 slowRunner 与 fastRunner 是否相等
if (slowRunner.equals(fastRunner)) {
System.out.println("The collisionNode is: " + slowRunner.val);
MyListNode fromHead = head;
while (!fromHead.equals(slowRunner)) {
fromHead = fromHead.next;
slowRunner = slowRunner.next;
}
return fromHead;
}
}
return null;
}
package MyList;
import java.util.HashMap;
import java.util.Random;
public class MyListTest {
public static void main(String[] args) {
MyListTest listTest = new MyListTest();
MyListNode head, end;
// 随机新建一个链表
HashMap<String, MyListNode> list = listTest.buildList(15);
// 获取链表的首位节点
head = list.get("head");
end = list.get("end");
// 打印这个链表看一看
listTest.display(head);
int k = 6;
// 获得倒数第6个节点
MyListNode node = listTest.nth2End(head, k);
System.out.printf("The %d-th node to the end is: %d\n", k, node.val);
// 此时链表是不存在环的
boolean ret1 = listTest.hasLoop(head);
System.out.println("\n" + ret1);
// 创建一个环,再判断含有环吗
end.next = node;
boolean ret2 = listTest.hasLoop(head);
System.out.println("\n" + ret2);
// 求其环的起始节点,看看是不是倒数第6个
MyListNode loopBegin = listTest.getLoopBegin(head);
System.out.println("The loopBegin is: " + loopBegin.val);
}
/** * 获取环的起始节点 */
public MyListNode getLoopBegin(MyListNode head) {
MyListNode slowRunner = head;
MyListNode fastRunner = slowRunner;
while (fastRunner != null) {
// slowRunner每次前进1步
slowRunner = slowRunner.next;
// fastRunner每次前进2步
// 每前进一步就进行判空,如果为空则说明到达链表的尾部,返回false
fastRunner = fastRunner.next;
if (fastRunner != null) {
fastRunner = fastRunner.next;
} else {
System.err.println("The list doesn't have loop!");
return null;
}
// 判断 slowRunner 与 fastRunner 是否相等
if (slowRunner.equals(fastRunner)) {
System.out.println("The collisionNode is: " + slowRunner.val);
MyListNode fromHead = head;
while (!fromHead.equals(slowRunner)) {
fromHead = fromHead.next;
slowRunner = slowRunner.next;
}
return fromHead;
}
}
return null;
}
public boolean hasLoop(MyListNode head) {
MyListNode slowRunner = head;
MyListNode fastRunner = slowRunner;
while (fastRunner != null) {
// slowRunner每次前进1步
slowRunner = slowRunner.next;
// fastRunner每次前进2步
// 每前进一步就进行判空,如果为空则说明到达链表的尾部,返回false
fastRunner = fastRunner.next;
if (fastRunner != null) {
fastRunner = fastRunner.next;
} else {
return false;
}
// 判断 slowRunner 与 fastRunner 是否相等
if (slowRunner.equals(fastRunner)) {
return true;
}
}
return false;
}
public MyListNode nth2End(MyListNode head, int k) {
// slowRunner 和 fastRunner 的初始化
MyListNode slowRunner = head;
MyListNode fastRunner = slowRunner;
for (int i = 0; i < k; i++) {
fastRunner = fastRunner.next;
if (fastRunner == null) {
return null;
}
}
// 遍历开始
while (fastRunner != null) {
fastRunner = fastRunner.next;
slowRunner = slowRunner.next;
}
return slowRunner;
}
/** * 创建节点数为num的单项链表,并保存其 首节点 和 尾部节点 */
public HashMap<String, MyListNode> buildList(int num) {
if (num < 1) {
return null;
}
MyListNode header = new MyListNode();
Random random = new Random();
int val;
MyListNode p = header;
for (int i = 0; i < num; i++) {
val = random.nextInt(100);
p.next = new MyListNode(val);
p = p.next;
}
HashMap<String, MyListNode> hashMap = new HashMap<>();
hashMap.put("head", header.next);
hashMap.put("end", p);
return hashMap;
}
/** * 打印链表 */
public void display(MyListNode list) {
System.out.println("***** Output the list: *****");
MyListNode p = list;
int count = 0;
while (p != null) {
System.out.printf("%-5d", p.val);
p = p.next;
count++;
if (count == 5) {
System.out.println();
count = 0;
}
}
}
}
输出
***** Output the list: *****
95 12 36 3 10
99 8 88 79 28
6 28 53 45 5
The 6-th node to the end is: 28
false
true
The collisionNode is: 53
The loopBegin is: 28