【java算法专场】双指针(上)

news2024/11/17 11:34:23

目录

前言

基本原理

对撞指针

快慢指针

移动零

算法思路

算法步骤

代码实现

算法分析

复写零

算法思路

算法步骤

代码实现

 快乐数

算法思路

算法步骤

代码实现

盛最多水的容器

​编辑算法思路

代码实现 


前言

双指针是一种在数组或链表等线性数据结构中高效解决问题的算法思想,适用于查找、排序、去重等场景。

基本原理

双指针的原理主要是利用两个指针(或索引)从不同起点出发,按照某种规则移动,直至满足某种终止条件

常见的双指针有两种形式:1、对撞指针,2、快慢指针

本篇主要讲解快慢指针。

对撞指针

一般用于顺序结构中,也称为左右指针

思路

  1. 对撞指针从两端向中间移动。一个指针从最左端开始,另一个从最右端开始,然后逐渐向中间逼近
  2. 对撞指针的终止条件一般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),即:

                      left==right(两个指针指向同一个位置)

                      left>right(两个指针错开)

快慢指针

⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列 结构上移动。对于处理环形链表或者数组非常有用。

不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快 慢指针的思想。

快慢指针的实现⽅式有很多种,最常⽤的⼀种就是: 在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。

移动零

算法思路

在本道题中,我们可以定义一个right来扫描数组,定义一个left,负责当right遇到非0数时,与left进行交换。当right遍历完数组后,也就将数组按照其相对顺序,把非0数移动到left之前,把所有0移动到left之后。

算法步骤

  1. 定义双指针left,right

  2. 让right先走

  3. 当right遇到非0数,与left进行交换,再让left++

  4. 当right遍历完数组,移动结束

 

代码实现

 /**
     * 交换数组中两个位置的元素。
     *
     * @param nums 输入的整数数组。
     * @param i 第一个位置的索引。
     * @param j 第二个位置的索引。
     */
    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    /**
     * 将数组中的所有零移动到末尾,同时保持非零元素的相对顺序不变。
     *
     * @param nums 输入的整数数组。
     */
    public void moveZeroes(int[] nums) {
        // 如果数组为空,则无需进行任何操作
        if (nums.length == 0) {
            return;
        }

        int left = 0;
        int right = 0;

        // 遍历数组,将非零元素依次交换到数组的左侧
        while (right < nums.length) {
            if (nums[right] != 0) {
                // 交换当前非零元素到左侧
                swap(nums, left, right);
                // 左指针向右移动,准备交换下一个非零元素
                left++;
            }
            // 右指针继续向右移动,扫描下一个元素
            right++;
        }
    }

算法分析

时间复杂度为O(N),只遍历了一遍数组,空间复杂度O(1),只用到了常数个变量。 

复写零

算法思路

利用双指针

  • cur指针:初始化为0,负责从数组的起始位置遍历数组,用于查找0元素并对新数组进行布局,记录新数组最后一个元素
  • dest指针:初始化为-1,当cur在遍历时,遇到0走两步,非0走1步,当dest走到数组末尾时,此时就能确定cur要复写的起始位置。当确定完cur要开始复写的位置,从后往前进行复写。

算法步骤

  1. 复写预备:首先通过cur对数组进行遍历,在dest越界的情况下,dest能到的位置为数组的最后一个元素位置,此时停止遍历。
  2. 处理dest越界情况:如果dest在数组倒数第二个元素位置,而此时cur下标的值为0,那么dest就会越界。由于数组已满,只能将数组最后一个元素复制为0,并相应调整cur和dest的位置(cur--,dest-=2)
  3. 从后往前复制:从dest出发,判断cur的值是否为0,为0,dest位置先复制为0,再dest--,再复制为0,再dest--,若cur位置的值不为0,则让cur位置的值给dest位置,dest--,当dest<0时,复制结束

代码实现

  /**
     * 复制数组中的零。
     * 将数组arr中的零复制一倍,并尽可能地填入数组中,可能会覆盖原数组的部分元素。
     * 如果复制零后,数组末尾无法再放置一个零,则将最后一个非零元素替换为零。
     * 此方法直接在原数组上进行操作,不返回新数组。
     *
     * @param arr 原数组,将在此数组上进行操作。
     */
    public void duplicateZeros(int[] arr) {
        // 获取数组长度
        int len = arr.length;
        // 初始化当前索引cur和目标索引dest
        int cur = 0;
        int dest = -1; // dest从-1开始,因为下一个要填入的位置是dest+1
        // 遍历数组,计算每个非零元素和零元素应该填入的位置
        int dest=-1;//dest从-1开始
        for (; cur < len; cur++) {
            // 如果当前元素不为零,目标索引移动一步
            // 如果当前元素为零,目标索引移动两步
            // 判断cur是否为0,若为0,dest移动2步,反之,移动1步
            if (arr[cur] != 0) {
                dest++;
            } else {
                dest += 2;
            }
            // 如果目标索引超出或等于数组长度减一,则无法再放置更多元素,结束循环
            // 判断dest是否到到数组末尾,若到达末尾,则说明不需要移动,直接返回
            if (dest >= len - 1) {
                break;
            }
        }
        // 检查是否需要将最后一个元素替换为零
        if (dest == len) {
            arr[len - 1] = 0;
            cur--;
            dest -= 2;
        }
        // 从后向前复制元素,实现零的复制
        // 进行复写
        while (dest >= 0) {
            // 如果当前元素为零,则在目标位置复制两个零
            if (arr[cur] == 0) {
                arr[dest--] = 0;
                arr[dest--] = 0;
            } else {
                // 如果当前元素不为零,则复制到目标位置
                arr[dest--] = arr[cur];
            }
            cur--;
        }
    }

时间复杂度为O(N),空间复杂度为O(1).

 快乐数

算法思路

快乐数的原理于链表是否成环相似。利用快慢指针即可解决。 根据上述的题⽬分析,我们可以知道,当重复执⾏ x 的时候,数据会陷⼊到⼀个「循环」之中。【快慢指针】有⼀个特性,就是在⼀个圆圈中,快指针总是会追上慢指针的,也就是说他们总会相遇在⼀个位置上。如果相遇位置的值是 1 ,那么这个数⼀定是快乐数;如果相遇位置不是1的话,那么就不是快乐数

算法步骤

  • isSum:主要用来求⼀个数 n 每个位置上的数字的平⽅和。
  • isHappy:用来判断一个数是不是快乐数
  1. 在isHappy中,首先定义两个指针:快指针fast和慢指针slow,slow初始化为n,fast初始化为isSum(n),即先进行一次计算。
  2. 循环判断:条件为(slow!=fast),在while循环中,slow每次调用一次isSum进行计算,而fast则每次调用两次isSum进行计算。当slow和fast的值相等时,则说明成环。
  3. 返回:当循环结束后,返回slow==1,即判断n是不是快乐数。

代码实现


    /**
     * 计算一个数字的各位平方和。
     * 
     * @param n 输入的整数
     * @return 各位数字的平方和
     */
    public int isSum(int n){
        int sum=0;
        while(n!=0){
            sum+=Math.pow(n%10,2);
            n=n/10;
        }
        return sum;
    }
    
    /**
     * 判断一个数字是否为“快乐数”。
     * “快乐数”是指在一系列操作后,最终得到1的数。
     * 操作是将数字的各位数的平方相加,然后重复这个过程。
     * 
     * @param n 输入的整数,用于判断是否为快乐数
     * @return 如果n是快乐数,返回true;否则返回false
     */
    public boolean isHappy(int n) {
        int slow=n;
        int fast=isSum(n);
        while(slow!=fast){
            slow=isSum(slow);
            fast=isSum(isSum(fast));//快指针走两步,慢指针走一步
        }
        return slow==1;
    }

代码时间复杂度为O(logN),空间复杂度为O(1).

盛最多水的容器

算法思路

  1. 设两个指针 left , right 分别指向容器的左右两个端点,此时容器的容积 : v = (right - left) * min( height[right], height[left]) 容器的左边界为 height[left] ,右边界为 height[right] 。
  2. 为了⽅便叙述,我们假设「左边边界」⼩于「右边边界」。
  3. 如果此时我们固定⼀个边界,改变另⼀个边界,⽔的容积会有如下变化形式:
  4.  容器的宽度⼀定变⼩。
  5.  由于左边界较⼩,决定了⽔的⾼度。如果改变左边界,新的⽔⾯⾼度不确定,但是⼀定不会超右边的柱⼦⾼度,因此容器的容积可能会增⼤。
  6.  如果改变右边界,⽆论右边界移动到哪⾥,新的⽔⾯的⾼度⼀定不会超过左边界,也就是不会过 现在的⽔⾯⾼度,但是由于容器的宽度减⼩,因此容器的容积⼀定会变⼩的。

由此可⻅,左边界和其余边界的组合情况都可以舍去。所以我们可以 left++ 跳过这个边界,继续去判断下⼀个左右边界。

当我们不断重复上述过程,每次都可以舍去⼤量不必要的枚举过程,直到 left 与 right 相遇。期间产⽣的所有的容积⾥⾯的最⼤值,就是最终答案.

代码实现

  /**
     * 计算两个非递减序列中形成的最大矩形区域的面积。
     * 该方法使用双指针技术来找到最大的矩形面积,避免了对每个可能的矩形进行遍历,提高了效率。
     * 
     * @param height 一个整数数组,表示每个位置的高度。
     * @return 返回最大的矩形面积。
     */
    public int maxArea(int[] height) {
        // 初始化左指针、面积为0,和右指针
        int left = 0;
        int V = 0;
        int right = height.length - 1;
        
        // 当左指针小于右指针时,进行循环
        while (left < right) {
            // 计算当前形成的矩形的最小高度
            int h = Math.min(height[left], height[right]);
            // 计算当前形成的矩形的宽度
            int len = right - left;
            // 更新当前的最大面积
            V = Math.max(V, h * len);
            
            // 根据左指针和右指针指向的高度,移动指针
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        
        // 返回最大面积
        return V;
    }

该算法的时间复杂度为O(N)只遍历了一遍数组,空间复杂度为O(1),只使用了常数个变量.

双指针上篇就先到这~

若有不足,欢迎指正~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1868060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GGUF模型转换入门

一、定义 1 定义 2 案例 二、实现 定义 GGUF是一种大模型文件格式&#xff0c;由开发者Georgi Gerganov提出。 这是一种针对大规模机器学习模型设计的二进制格式文件规范。它的主要优势在于能够将原始的大模型预训练结果经过特定优化后转换成这种格式&#xff0c;从而可以更…

jpg压缩的快速方法,分享4个!

在数字化时代&#xff0c;图片已成为我们生活和工作中不可或缺的一部分。然而&#xff0c;高质量的图片往往伴随着较大的文件大小&#xff0c;这在一定程度上影响了网页的加载速度和用户体验。为了解决这一问题&#xff0c;我们为大家精心挑选了4款jpg压缩软件&#xff0c;让你…

3种电脑截屏的快捷方式,告别繁琐操作,你值得拥有

无论是记录重要信息、分享有趣瞬间&#xff0c;还是制作教程和报告&#xff0c;截屏都是不可或缺的工具。当你想要迅速捕捉屏幕上的精彩瞬间&#xff0c;却发现不知如何截屏&#xff0c;是不是感到有些头疼&#xff1f;今天&#xff0c;就让小编揭晓3种电脑截屏的快捷方式&…

赏金猎人src挖掘入门

文章目录 1. 什么是漏洞2. OWASP Top 103. 利用的漏洞来源4. SRC安全应急响应中心5. Burpsuite简介6. 浏览器代理插件6.1 firefox浏览器代理插件6.2 edge浏览器代理插件3.chrome浏览器代理插件&#xff08;需要科学上网&#xff09; 1. 什么是漏洞 漏洞是指一个系统存在的弱点或…

springboot社区维修平台

设计技术&#xff1a; springboot、mysql、maven、前端vue 主要功能&#xff1a; 住户管理、社区公告管理、维修工管理、维修订单管理、接单信息管理、订单信息管理、在线沟通管理、举报信息管理、留言板管理、系统管理等功能模块。 管理员功能模块 管理员通过后台登录页面…

【知识学习】阐述Unity3D中Stencil的概念及使用方法示例

在Unity3D中&#xff0c;Stencil&#xff08;模板&#xff09;是一种高级的图形渲染技术&#xff0c;它允许开发者对渲染过程进行精细控制。Stencil Buffer是附加在颜色缓冲区和深度缓冲区之外的另一个缓冲区&#xff0c;它可以用来存储每个像素是否应该被渲染的信息。 Stenci…

记录samba账号操作日志,增删改查等(安全审计)

说明&#xff1a;windows用户映射samba文件共享服务&#xff0c;记录samba账号的操作日志 只要三步&#xff01; 安装必要软件包 audit配置samba共享配置Syslog 具体步骤 1. 安装必要的软件包 audit 是linux系统的高级审计框架 主要功能&#xff1a;系统调用监控、文件和目…

安科瑞智能物联网远传电表的优势

物联网远传智能电表是一种新型的电表&#xff0c;它通过物联网技术实现了电能的远程监测和管理。下面是物联网远传智能电表的优缺点&#xff1a;王盼盼&#xff1b;18721098782/Acrel 优点&#xff1a; 1. 实现了电能的远程监测和管理&#xff0c;可以随时随地了解电能的使用…

【EtherCAT】TwinCAT3通过PLC修改SDO数据

目录 1、打开twincat3, 左边PLC右键->添加新项&#xff0c;建立PLC工程 2、->References右键添加库 3、找到Tc2_EtherCAT库&#xff0c;点确定。 4、PLC程序ST语言就可以调用下面的功能块函数 5、PLC编程界面右键->输入助手 1、打开twincat3, 左边PLC右键->添…

7月开始,考研数学0️⃣基础线代30天满分规划

线代零基础&#xff1f; 那千万不要去跟李永乐老师的线代课程&#xff0c;因为李永乐老师的线代课程比较进阶&#xff0c;适合有一定基础的同学去听&#xff0c;下面这两位才是零基础线代的神&#xff01; 一个是喻老&#xff0c;另外一个是汤家凤&#xff01; 这两个老师的…

目标检测系列(四)利用pyqt5实现yolov8目标检测GUI界面

目录 1、pyqt5安装 2、PyCharm添加Qt Designer、PyUIC 3、Qt Designer设计界面 4、根据ui文件自动生成py文件 5、修改py文件来调用检测程序 6、执行py文件启动 1、pyqt5安装 Qt Designer&#xff1a;一个用于创建图形用户界面的工具&#xff0c;可轻松构建复杂的用户界面…

JOSEF约瑟 JOXL-J拉绳开关 整定范围宽

用途 双向拉绳开关的壳体采用金属材料铸造&#xff0c;具有足够的机械强度,抵抗并下工作时脱落的岩石&#xff0c;爆块等物体的撞击不被破坏&#xff0c;当胶带输送机发生紧急事故时&#xff0c;启动拉绳开关,可立即停机报警&#xff0c;防止事故的扩大,保证工作现场的人身安全…

js获取字符串中超链接,并加样式跳转页面

效果图 主要代码&#xff1a;js this.$nextTick(() > {// 给循环出来的div标签加个id为let container document.getElementById("linkTo");container.innerHTML container.textContent.replace(/(https?:\/\/[^\s])/g, function (match) {var link documen…

代码随想录算法训练营:14/60

非科班学习算法day14 | LeetCode266:翻转二叉树 &#xff0c;Leetcode101: 对称二叉树&#xff0c;Leetcode100:相同的的树 &#xff0c;LeetCode572:另一颗树的子树&#xff0c;LeetCode104&#xff1a;二叉树的最大深度&#xff0c;LeetCode559&#xff1a;N叉树的最大深度 目…

将产品制作成3D模型在网站上展示需要多少费用?

将产品制作成3D模型并在网站上展示的费用会因多种因素而异&#xff0c;包括模型的复杂度、所需的细节程度、制作3D模型的软件和工具、以及是否需要专业设计师的服务等。此外&#xff0c;不同的3D模型制作服务提供商可能会有不同的定价标准。 如果能自己制作3D模型&#xff0c;…

精益生产推进时如何营造持续变革的氛围?

在快速变化的市场环境中&#xff0c;企业如何保持竞争力&#xff1f;精益生产无疑为众多企业提供了一个强大的战略工具。但是&#xff0c;单纯的引入精益生产理念和方法并不能保证企业的持续成功。关键在于如何营造一种持续变革的氛围&#xff0c;让精益生产成为推动企业不断前…

职升网:咨询工程师可以的工作都有啥?

(1)经济社会发展规划、计划咨询; (2)行业发展规划和产业政策咨询; (3)经济建设专题咨询; (4)投资机会研究; (5)工程项目建议书的编制; (6)工程项目可行性研究报告的编制; (7)工程项目评估; (8)工程项目融资咨询&#xff0c;绩效追综评价&#xff0c;后评价及培训咨询服务…

零信任价值获全面认可 新场景下展现无穷潜力

2023年&#xff0c;零信任在全球范围内持续快速发展&#xff0c;已经从新的安全理念发展成为云时代的主流安全架构&#xff0c;进入了全面普及期。 2023年&#xff0c;中国零信任市场同样涨势迅猛&#xff0c;产业生态越来越成熟&#xff0c;应用范围越来越广&#xff0c;应用…

【数据库】oracle安装步骤(详细)

目录 1.下载oracle安装包 2.解压文件后&#xff0c;找到可执行安装文件【 setup.exe 】双击安装。如图&#xff1a; 3.安装第一步&#xff1a;配置安全更新&#xff0c;这步可将自己的电子邮件地址填写进去&#xff08;也可以不填写&#xff0c;只是收到一些没什么用的邮件而…

高中数学:不等式-常用不等式知识点汇总

一、基本性质 比较大小的常用两种方法&#xff1a;作差法&#xff0c;作商法 等式性质 不等式性质 二、基本(均值)不等式 扩展 三、二次函数与一元二次方程不等式 定义 解的对应关系 一元二次不等式的求解过程 四、二元一次不等式(组)与线性规划 关键在于求多个不等…