首页 > 技术文章 > 还学不会快慢指针法,你来揍我!

ccxyyds 2021-05-30 08:13 原文

​            hello,大家好,我是小慕呀。 作为一个互联网搬砖工程师,要想把搬砖的效率提高,我们肯定是逃不掉数据结构和算法知识的,这不,可爱的小慕今天就和大家一起学习来了,今天给大家分享的是快慢指针,那啥是快慢指针呢,小慕简单的介绍一下

 

快慢指针就是定义两根指针,移动的速度一快一慢,以此来制造出自己想要的差值。这个差值可以让我们找到链表上相应的节点。

那快慢指针可以解决哪些实际问题呢,接下来我们一起看看吧!

一、使用快慢指针来找到链表的中点

  1. 首先我们设置两个指针slow和fast,这2个指针的初始位置相同,都指向链表的头结点,然后slow指针每次移动一步,fast指针每次移动两步;

  2. 如果链表中节点个数为偶数时,当快指针无法继续移动时,慢指针刚好指向中点;如果链表中节点个数为奇数时,当快指针走完,慢指针指向中点的前一个节点。

public ListNode reverseStore(ListNode head) {        // 初始化,让快指针和慢指针都处于链表的头部        ListNode slow=head;        ListNode fast=head;        while(fast!=null&&fast.next!=null){//如果快指针并且下一个不为空            fast=fast.next.next;//快指针移动两个            slow=slow.next;//慢指针移动一个               }        return slow;  }

 

二.利用快慢指针来判断链表中是否有环

问题描述: 给定一个链表,判断链表中是否有环。

以下两种情况都属于链表中存在环,“0”字型和“6”字型

 

这个时候我们的解决方案就是利用“快慢指针”, 慢指针针每次走一步,快指针每次走两步,如果相遇就说明有环,如果有一个为空说明没有环。代码比较简单  (在代码下面会专门讲解思路的,放心!)

public boolean hasCycle(ListNode head) {     if (head == null)        return false;     //快慢两个指针,初始时都指向链表的头结点     ListNode slow = head;     ListNode fast = head;     while (fast != null && fast.next != null) {         //慢指针每次走一步         slow = slow.next;        //快指针每次走两步        fast = fast.next.next;        //如果相遇,说明有环,直接返回true        if (slow == fast)           return true;    }    //否则就是没环    return false;}

 

到这里大家可能就有疑问了,为什么快慢指针就一定能判断是否有环。我们可以这样来思考一下,假如有环,那么快慢指针最终都会走到环上,假如环的长度是m,快慢指针最近的间距是n,如下图中所示

下面说的两点相距是指 “快指针还需要多远可以再次追到慢指针”

 

快指针每次走两步,慢指针每次走一步,所以每走一次快慢指针的间距就要缩小一步,在图一中当走n次的时候就会相遇,在图二中当走m-n次的时候就会相遇。这样就解释清楚了读者的疑问了。

 

 

三、删除链表的倒数第n个节点

问题描述:删除倒数第n个节点,那就等于是要我们先找出待删除元素前一个元素,也就是第n-1个节点。聪明的你肯定发现了,我们又把这个问题转化为找链表上的某个节点的问题了,这是快慢指针最擅长的场景。

(小慕想说,其实这个问题跟第一个问题的处理思路没什么区别,换汤不换药O(∩_∩)O哈哈~

 

思路很简单,我们一开始就让fast指针比slow指针快n+1个元素,接下来,两个指针都是一步一步来往下走。那么当fast指针走完时,slow指针就刚刚好停留在第(n-1)个元素上。

//删除链表倒数第n个节点public ListNode removeBackEndNode(ListNode head, int n) {        if (head == null || head.next == null) {            return null;        }        ListNode dummyHead = new ListNode(-1);        dummyHead.next = head;       //初始时让快慢指针都指向链表的头部        ListNode slow = dummyHead;        ListNode fast = dummyHead;        for (int i = 0; i < n + 1; i++) {            fast = fast.next;        }        while (fast != null) {            slow = slow.next;            fast = fast.next;        }        slow.next = slow.next.next;  //为了解决断链问题        return dummyHead.next;    }

 

四、判断是否是回文链表

快指针每次前进两个单位,慢指针每次前进一个单位并修改其next节点指向前一个节点,所以当快指针到达链表末尾的时候(空节点或空节点的前一个节点),慢指针刚好在中间,如下图所示

 

此时慢指针继续向后走,同时开辟一个新节点往前走,看下图

 

 

判断链表中点前后是否相同,达到判断回文串的目的,如下图所示

 

代码如下:

/** * Definition for singly-linked list. * public class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { val = x; } * } */class Solution {    public boolean isPalindrome(ListNode head) {        if(head == null || head.next == null){            return true;        }        ListNode pre = null;//指向前一个节点的指针        ListNode slow = head;//慢指针        ListNode fast = head;//快指针        while(fast!=null&&fast.next!=null){            fast = fast.next.next;            ListNode next = slow.next;            slow.next = pre;//修改慢指针走过的节点指向前一个节点            pre = slow;            slow = next;        }        if(fast != null){            slow = slow.next;            //当长度为奇数是,慢指针需要再走一个单位        }        while(pre!=null) {            //判断是否为回文串            if(pre.val!=slow.val){                return false;            }            pre = pre.next;            slow = slow.next;        }        return true;    }}

 

好啦,今天的内容到这里就结束了,祝大家好梦哦!

     历史推文

    都2021年了,你还不懂幂等性问题的解决方案?

 为啥Spring事务失效了,你踩坑了吗?

    今天你又被内卷了吗?“躺平”好不好?

        Eureka和Zookeeper到底有什么区别 ?

     听说你还不懂什么是CAP理论?

     TCP三次握手、四次挥手很难?NO

  位运算的奇“赢”技巧

 

半山腰总是最挤的,你得去山顶上瞅瞅,拜拜

好,今天的内容就分享到这里了,你们一定要变优秀哦,我们下期再见。

       接下来的一段时间,我会专注Java技术栈,计算机网络,数据结构和算法,操作系统,设计模式,计算机组成原理,数据库原理,设计模式来做分享,欢迎你们和我一起学习,一起提高,Fighting!
 

 

 

推荐阅读