快慢指针
文章目录
常见模式
跟链表相关
判断链表是否有环 等等
第一道 环形链表
思路:
假如该链表是循环链表,那我们可以定义两个指针,一个每次向前移动两个节点,另一个每次向前移动一个节点。这就和田径比赛是一样的,假如这两个运动员跑的是直道,那快的运动员和慢的运动员在起点位于同一位置,但快的运动员必将先到达终点,期间这两个运动员不会相遇。而如果绕圈跑的话(假设没有米数限制),跑的快的运动员在超过跑的慢的运动员一圈的时候,他们将会相遇,此刻就是循环链表。
与龟兔赛跑的不同:
关于快慢指针中两个指针的速度问题: 和龟兔赛跑问题不同的是,龟兔赛跑是一个连续性的问题,无论二者的速度差是多少,可以这样假设:假设赛道长度为s,v_f表示速度快的值,v_s表示速度慢的值,(假设二者初始位置相同),那么可以求出来:(v_f-v_s)t=s;这样求出来的t,是二者第一次相遇的时间; 本题不同的是:对于链表来说是一个离散的值,我们假设环内共有n个节点,同样假设快指针与慢指针分别是v_f,v_s;如果想要相遇(假设初始位置相同),同样有(v_f-v_s)k = n; ——这个时候 v_f,v_s 为正整数,k为循环次数,n为节点数目; k = n/(v_f-v_s)如果想要k为整数,那么可以看到二者的速度差是有要求的,必须能够被n整除;注意:这样求得是第一次相遇,也有可能v_f-v_s是n的整数倍;
代码:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null)
return false;
ListNode p=head;
ListNode q=head;
while(p!=null&&q!=null)
{
p=p.next;
if(q.next==null)
return false;
q=q.next.next;
if(p.equals(q))
{
return true;
}
}
return false;
}
}
第二道 环形链表
思路:
如图设非环形有a个节点
设环形有b个节点
第一次相遇时
fast走的节点数=2*slow走的节点数\\
fast走的节点数-slow走的节点数=nb(多走了n圈)
第一次相遇时slow走的节点数为nb
一个节点走a+nb步一定会在入口处
slow在第一次相遇后再走a步即可
可以第一次相遇后,设置一个指针从头开始,以slow的速度,两者相遇时即为入口
代码:
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head==null)
return null;
ListNode p=head;
ListNode q=head;
if(head.next==null)
{
return null;
}
while(p!=null&&q!=null)
{
p=p.next;
if(q.next==null)
return null;
q=q.next.next;
if(p.equals(q))
{
q=head;
while(p!=null&&q!=null)
{
if(p.equals(q))
{
return p;
}
q=q.next;
p=p.next;
}
}
}
return null;
}
}
第三道 链表的中间结点
思路:
快指针一次跨两个节点;
慢指针一次跨一个节点;
快指针到结尾时,慢指针到中位。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(true)
{
if(fast.next==null)
{
return slow;
}
if(fast.next.next==null)
{
slow=slow.next;
return slow;
}
else {
fast=fast.next.next;
slow=slow.next;
}
}
}
}
第四道 删除链表倒数第n个节点
思路:
快指针先行n个节点
然后快慢指针一步一个节点向下
快指针到达结尾时
慢指针到达的位置即为倒数第n个节点
代码:
public static ListNode deleteNode(ListNode head,int n) {
ListNode p=head;
ListNode q=head;
for(int i=0;i<n-1;i++)
{
p=p.next;
}
while(true)
{
if(p.next.next==null)
{
q.next=q.next.next;
return head;
}
p=p.next;
q=q.next;
}
}