教你如何轻松学会Java快慢指针法

一、什么是快慢指针?

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

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

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

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个节点。聪明的你肯定发现了,我们又把这个问题转化为找链表上的某个节点的问题了,这是快慢指针最擅长的场景。

思路很简单,我们一开始就让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;
    }
}

到此这篇关于教你如何轻松学会Java快慢指针法的文章就介绍到这了,更多相关Java快慢指针法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何用Java实现排列组合算法

    需求 我们的数据表有多个维度,任意多个维度组合后进行 group by 可能会产生一些"奇妙"的反应,由于不确定怎么组合,就需要将所有的组合都列出来进行尝试. 抽象一下就是从一个集合中取出任意元素,形成唯一的组合.如[a,b,c]可组合为[a].[b].[c].[ab].[bc].[ac].[abc]. 要求如下: 组合内的元素数大于 0 小于等于 数组大小: 组合内不能有重复元素,如 [aab] 是不符合要求的组合: 组合内元素的位置随意,即 [ab] 和 [ba] 视为同一种组合:

  • 分析Java非阻塞算法Lock-Free的实现

    非阻塞的栈 我们先使用CAS来构建几个非阻塞的栈.栈是最简单的链式结构,其本质是一个链表,而链表的根节点就是栈顶. 我们先构建Node数据结构: public class Node<E> { public final E item; public Node<E> next; public Node(E item){ this.item=item; } } 这个Node保存了内存item和它的下一个节点next. 然后我们构建非阻塞的栈,在该栈中我们需要实现pop和push方法,我们

  • java算法之静态内部类实现雪花算法

    概述 在生成表主键ID时,我们可以考虑主键自增 或者 UUID,但它们都有很明显的缺点 主键自增:1.自增ID容易被爬虫遍历数据.2.分表分库会有ID冲突. UUID: 1.太长,并且有索引碎片,索引多占用空间的问题 2.无序. 雪花算法就很适合在分布式场景下生成唯一ID,它既可以保证唯一又可以排序.为了提高生产雪花ID的效率, 在这里面数据的运算都采用的是位运算 一.概念 1.原理 SnowFlake算法生成ID的结果是一个64bit大小的整数,它的结构如下图: 算法描述: 1bit 因为二进

  • Java实现雪花算法的原理

    SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法.其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id.在分布式系统中的应用十分广泛,且ID 引入了时间戳,基本上保持自增的,后面的代码中有详细的注解. 这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号. 给大家举个例子吧,比如下面那个 64 bit 的 long 型数字: 第一个部分

  • java算法之余弦相似度计算字符串相似率

    概述 功能需求:最近在做通过爬虫技术去爬取各大相关网站的新闻,储存到公司数据中.这里面就有一个技术点,就是如何保证你已爬取的新闻,再有相似的新闻 或者一样的新闻,那就不存储到数据库中.(因为有网站会去引用其它网站新闻,或者把其它网站新闻拿过来稍微改下内容就发布到自己网站中). 解析方案:最终就是采用余弦相似度算法,来计算两个新闻正文的相似度.现在自己写一篇博客总结下. 一.理论知识 先推荐一篇博客,对于余弦相似度算法的理论讲的比较清晰,我们也是按照这个方式来计算相似度的.网址:相似度算法之余弦相

  • Java基础之八大排序算法

    前言 关系 复杂度 一.直接插入排序 基本思想: 将新的数据插入已经排好的数据列中. 将第一个和第二个数排序,构成有序数列 然后将第三个数插进去,构成新的有序数列,后面的数重复这个步骤 算法描述 1.设定插入的次数,即是循环次数,for(int i=1;i<length;i++),1个数的那次不用插入. 2.设定插入的数和得到的已经排好的序列的最后一个数,insertNum和j=i-1. 3.从最后一个数向前开始循环,如果插入数小于当前数就将当前数向前移动一位 4.将当前位置放置到空的位置,即j

  • Java排序算法之选择排序

    一.选择排序 选择排序就是在每一次遍历过程中将数组中值最小的排到当前的第一位. 总共需要(数组长度-1)次遍历,在每次遍历中假定第一位索引的值为最小值,然后与下一个值对比,如果最小索引所在值大于其他值就将小的那一个索引当作最小值索引,接着继续对比最小索引所在值与下一个索引的值,重复此操作,最终就会在此次遍历中得到最小值及其索引,将最小值与第一位的值进行交换,这样就将最小值放到了数组开头,完成本次遍历. 选择排序的时间复杂度为O(N^2) 二.代码实现 package com.example.al

  • Java 手写LRU缓存淘汰算法

    概述 LRU 算法全称为 Least Recently Used 是一种常见的页面缓存淘汰算法,当缓存空间达到达到预设空间的情况下会删除那些最久没有被使用的数据 . 常见的页面缓存淘汰算法主要有一下几种: LRU 最近最久未使用 FIFO 先进先出置换算法 类似队列 OPT 最佳置换算法 (理想中存在的) NRU Clock 置换算法 LFU 最少使用置换算法 PBA 页面缓冲算法 LRU 的原理 LRU 算法的设计原理其实就是计算机的 局部性原理(这个 局部性原理 包含了 空间局部性 和 时间

  • 教你用Java实现RSA非对称加密算法

    一.非对称加密 非对称加密算法是一种密钥的保密方法. 非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥).公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密.因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法. 非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将公钥公开,需要向甲方发送信息的其他角色(乙方)使用该密钥(甲方的公钥)对机密信息进行加密后再发送给甲方:甲方再用自己私钥对加密

  • 教你如何轻松学会Java快慢指针法

    一.什么是快慢指针? 快慢指针就是定义两根指针,移动的速度一快一慢,以此来制造出自己想要的差值.这个差值可以让我们找到链表上相应的节点. 那快慢指针可以解决哪些实际问题呢,接下来我们一起看看吧! 二.使用快慢指针来找到链表的中点 1.首先我们设置两个指针slow和fast,这2个指针的初始位置相同,都指向链表的头结点,然后slow指针每次移动一步,fast指针每次移动两步: 2.如果链表中节点个数为偶数时,当快指针无法继续移动时,慢指针刚好指向中点:如果链表中节点个数为奇数时,当快指针走完,慢指

  • 教你5分钟学会用requirejs(必看篇)

    requirejs是干啥的啊? 曾经,我们将一些js组件放到不同的文件,然后通过script标签引入,如果几个组件有依赖,那么要小心了,你必须将被依赖的放到前面,否则的话会出现啥啥啥is undefined或者啥啥啥is not a function之类的错误.比如一个jquery的插件显然是依赖jquery核心库的,所以jquery核心库文件必须先引入.项目小组件少依赖简单还好,要是项目大组件多依赖复杂就糟糕了.咋办?用requirejs啊

  • 手把手教你SpringBoot轻松整合Minio

    前言 使用Spring Boot 可以非常方便.快速搭建项目,使我们不用关心框架之间的兼容性,适用版本等各种问题,我们想使用任何东西,仅仅添加一个配置就可以. 提示:以下是本篇文章正文内容,下面案例可供参考 一.技术介绍 1.Minio是什么? MinIO 是一个基于Apache License v2.0开源协议的对象存储服务.它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片.视频.日志文件.备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5

  • 时间轻松学会.NET Core操作ElasticSearch7的方法

    在互联网上,随处可见的搜索框.背后所用的技术大多数就是全文检索. 在全文检索领域,常见的库/组件有:Lucene.Solr.Sphinx.ElasticSearch等. 简单对比几种全文引擎的区别 Lucene是一个基于Java开发的全文检索基础包,使用起来繁杂,且默认不支持分布式检索 Solr是基于Lucene开发的一个搜索工具.抽象度更高,使用更简单,且提供一个控制面板. ElasticSearch也是基于Lucene开发的.同样是高度抽象,并提供了一个非常强大的DSL检索功能,可以很方便的

  • 教你如何用好 Java 中的枚举

    目录 1.概览 2.自定义枚举方法 3.使用 == 比较枚举类型 4.在 switch 语句中使用枚举类型 6.EnumSet and EnumMap 6.1. EnumSet 6.2. EnumMap 7. 通过枚举实现一些设计模式 7.1 单例模式 7.2 策略模式 8. Java 8 与枚举 9. Enum 类型的 JSON 表现形式 10. 补充 1.概览 enum关键字在 java5 中引入,表示一种特殊类型的类,其总是继承java.lang.Enum类,更多内容可以自行查看其官方文档

  • 一篇文章学会java死锁与CPU 100%的排查

    目录 01 Java死锁排查和解决 1.啥是死锁? 2.为啥子会出现死锁? 3.怎么排查代码中出现了死锁?[重点来了] 第一个姿势:使用 jps + jstack 第二个姿势:使用jconsole 第三个姿势:使用Java Visual VM 4.如何避免死锁? 02.Java CPU 100% 排查技巧 第一个姿势,步骤有点多,难度四星 第二个姿势,待开发[奸笑脸] 03 推荐两个高效排查问题工具 04 总结 05 彩蛋-另一个姿势 00 本文简介 作为一名搞技术的程序猿或者是攻城狮,想必你应

  • 一篇文章让你三分钟学会Java枚举

    什么是枚举 至于枚举,我们先拿生活中的枚举来入手,然后再引申Java中的枚举,其实它们的意义很相似. 谈到生活中的枚举,假如我们在玩掷骰子的游戏,在我们手中有两个骰子,要求掷出两个骰子的点数和必须大于6的概率,那么在此情此景,我们就需要使用枚举法一一列举出骰子点数的所有可能,然后根据列举出来的可能,求出概率. 可能有的小伙伴发现,这就是数学啊?这就是数学中的概率学和统计学.对,我们的枚举法就是常用于概率统计中的. 枚举类enum是jdk1.5引入的,全称enumeration,和class.in

  • 轻松了解java中Caffeine高性能缓存库

    目录 轻松lCaffeine 1.依赖 2.写入缓存 2.1.手动写入 2.2.同步加载 2.3.异步加载 3.缓存值的清理 3.1.基于大小的清理 3.2.基于时间的清理 3.3.基于引用的清理 4.缓存刷新 5.统计 轻松lCaffeine 1.依赖 我们需要将Caffeine依赖添加到我们的pom.xml中: <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId&g

  • 教你用JDK编译Java文件的方法

    1.下载并安装好JDK百度网盘链接: 链接: https://pan.baidu.com/s/1j_TSvjHgw3jEBGaMk0LQ8w?pwd=pdp9 提取码: pdp9 2.设置环境变量 第一步:将JDK的安装路径bin进行复制,如:D:\Java\jdk1.8.0_221\bin 第二步:在高级选项卡中,点击环境变量按钮---->在系统变量中找Path---->选中Path点击编辑---->点击新建---->按Ctrl+V---->确定 3:打开记事本编写Java

  • Java剑指offer之删除链表的节点

    目录 1.简述 2.代码实现 1.简述 描述: 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点.返回删除后的链表的头节点. 1.此题对比原题有改动 2.题目保证链表中节点的值互不相同 3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点 数据范围: 0<=链表节点值<=10000 0<=链表长度<=10000 示例1 输入: {2,5,1,9},5 返回值: {2,1,9} 说明: 给定你链

随机推荐