快慢指针的应用
2019-07-09 本文已影响0人
YocnZhao
什么是快慢指针:快慢指针是链表操作中的常用操作,最经典的应用是判断单链表中是否有环。
判断单链表是否存在环
两个指针fast和slow,fast一次移动两次,slow一次移动一次,如果链表存在环,这两个指针一定会在某个节点碰头。请看下图,一共6次循环让快慢指针相遇。
一段代码解释:
public static boolean hasLoop(SingleNode node) {
if (node == null || node.getNext() == null) {
return false;
}
//快指针移动两次,初始化到next
SingleNode fast = node.getNext();
SingleNode slow = node;
while (fast != null) {
//快慢指针相遇,说明有环
if (fast == slow) {
return true;
}
if (fast.getNext() != null) {
fast = fast.getNext().getNext();
} else {
//快指针的next为null,说明没有环
return false;
}
slow = slow.getNext();
}
return false;
}
当然,快慢指针不止只能判断单链表是否有环,还有一些其他的应用,这里我们列举几个
单链表是否存在环,如果存在,找到环入口。
思路:还是上面检测是否有环的快慢指针,如果有环则快慢指针相遇,相遇后快指针指向head,然后快慢指针都变成每次移动一步,相遇的地方就是入口。
这其实是个数学问题,我们用下面的图来解释。
我们知道fast走两步,slow走一步,按照上图,fast跟slow在K点第一次相遇,这个时候:快慢指针分别走了多少步呢?
slow=a+b
fast=a+b+c+b
而且我们知道fast走了slow的两倍:
fast=slow*2
结合上面两个推论 a+b+c+b=(a+b)*2 => a=c
我们能得到a=c,也就是绿线跟红线一定是相等的。
所以快慢指针第一次相遇的时候,让快指针或者一个新的指针从head出发,两指针以相同的速度出发,一定能在入口相遇。
/**
* 判断是否有环,如果有得到入口
* 思路:如果有环,快慢指针会相遇,这时候把快指针赋值给head,快慢指针同时加一,相遇的时候就是指针入口
*
* @param root head
* @return 入口Node
*/
private SingleNode getEntrance(SingleNode root) {
SingleNode fast = root;
SingleNode slow = root;
boolean front = false;
while (fast != null && fast.getNext() != null) {
fast = fast.getNext().getNext();
slow = slow.getNext();
if (fast == slow) {
front = true;
break;
}
}
if (!front) {
return null;
}
slow = root;
while (slow != fast) {
slow = slow.getNext();
fast = fast.getNext();
}
return fast;
}
在有序链表中寻找中位数
快指针走两步,慢指针走一步,当快指针到达尾节点的时候,慢指针到达中间。
public SingleNode middleNode(SingleNode head) {
if (head == null) return null;
SingleNode fast = head, slow = head;
while (fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (fast == null) {
return slow;
}
}
return slow;
}
输出链表中的倒数第K个节点(即正数第length-K个节点)
/**
* 输出链表中的倒数第K个节点(即正数第n-K个节点)
* 思路:两个指针,指针一先走k个node,指针二开始走,指针一到尾部的时候,指针二到达倒数第k个节点
*
* @param node 目标链表的头结点
* @param k 需要找的节点
* @return 节点
*/
public SingleNode getReverseNumK(SingleNode node, int k) {
if (k >= NodeUtil.getLinkedListLength(node)) {
return null;
}
SingleNode fastNode = node;
SingleNode slowNode = node;
int pointer = 0;
while (fastNode != null) {
fastNode = fastNode.getNext();
if (pointer >= k) {
slowNode = slowNode.getNext();
}
pointer++;
}
return slowNode;
}
判断两个单链表是否相交,如果相交,找到他们的第一个公共节点
这里提供一种思路,两个链表如果有相同的节点说明相交,换句话说两个链表有一段公共部分
如上图所示,链表一(5->6->7->8->9)跟链表二(1->2->3->4->7->8->9)共享了789节点,我们声明两个指针。
指针一遍历56789,结束后再遍历12347
指针二遍历1234789,结束后遍历567
①->5678912347
②->1234789567
我们发现这个时候7节点已经相等了,说明相交了。
/**
* 判断两个链表是否相交,如果相交得到交点
* 两个指针分别指向链表1链表2,指针一到结尾后指向链表2,指针二结尾后指向链表1,如果出现重合,说明相交。
*
* @param leftList 链表1
* @param rightList 链表2
*/
private SingleNode getIntersect1(SingleNode leftList, SingleNode rightList) {
SingleNode pointer1 = leftList;
SingleNode pointer2 = rightList;
boolean pointer1End = false;
boolean pointer2End = false;
while (pointer1 != null) {
if (pointer1 == pointer2) {
return pointer1;
}
if (!pointer1End && pointer1.getNext() == null) {
pointer1End = true;
pointer1 = rightList;
} else {
pointer1 = pointer1.getNext();
}
if (!pointer2End && pointer2.getNext() == null) {
pointer2End = true;
pointer2 = leftList;
} else {
pointer2 = pointer2.getNext();
}
}
return null;
}