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

目录

前言

基本原理

对撞指针

快慢指针

移动零

算法思路

算法步骤

代码实现

算法分析

复写零

算法思路

算法步骤

代码实现

 快乐数

算法思路

算法步骤

代码实现

盛最多水的容器

​编辑算法思路

代码实现 


前言

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

基本原理

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

常见的双指针有两种形式: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和目标索引destint 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.mzph.cn/pingmian/36055.shtml

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

相关文章

Axure 教程 | 雅虎新闻焦点

主要内容 在雅虎首页&#xff0c;新闻焦点大图和焦点小图同步切换轮播&#xff0c;本课程我们来学习如何实现这个效果。 交互说明 1.页面载入后&#xff0c;切换当前屏幕显示的5张焦点图&#xff0c;小图标处以横线提示当前焦点图。 2.鼠标移入焦点大图&#xff0c;新闻标题显示…

马斯克的SpaceX星舰有多牛?我们离殖民火星还有多远?

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 埃隆马斯克是一位知名的企业家和工程师&#xff0c;他掌握着多家公司&#xff0c;涉及多个领域&#xff0c;包括电动汽车、太空探索、太阳能、脑…

Charles 证书迁移/复制,实现手机安装一次证书可以连接多个设备的 charles 效果

如果你希望在一个手机上安装一次证书&#xff0c;但是使用这个手机可能在不同的时候去连接你自己安装了Charles的不同设备。比如你在公司有有一个电脑&#xff0c;你在家里也有一个电脑&#xff0c;甚至还有一个笔记本等。 如果想实现只给手机安装一次证书&#xff0c;就可以都…

告别数据线!轻松实现iOS和安卓设备间的文件共享

用 AirDroid 的附近传输功能&#xff0c;完全免费&#xff0c;几十个G的文件也可以相互传输。不限制iPhone和iPad数量&#xff0c;多个设备同时登录也不会强迫下线。 当你要在苹果手机和安卓手机之间传输文件&#xff0c;请将AirDroid安装到两台手机上&#xff0c;然后登录同一…

[AI开发配环境]VSCode远程连接ssh服务器

文章目录 总览&#xff1a;ssh连接远程服务器连接免密登录&#xff1a;Docker&#xff1a;ssh连接远程宿主机后&#xff0c;进一步连接并使用其中的docker容器reload window 配置解释器&#xff1a;CtrlP&#xff0c;在上面输入“>python”, 然后选selecet interpreter运行命…

Ubuntu磁盘分区和挂载 虚拟机扩容 逻辑卷的创建和扩容保姆及教程

目录 1、VMware虚拟机Ubuntu20.04系统磁盘扩容 2、Linux的磁盘分区和挂载 3、创建逻辑卷和逻辑卷的扩容 1、VMware虚拟机Ubuntu20.04系统磁盘扩容 通过下图可以看出我们的根磁盘一共有20G的大小&#xff0c;现在我们把它扩容为30G 注&#xff1a;如果你的虚拟机有快照是无…

CV每日论文--2024.6.26

1、StableNormal: Reducing Diffusion Variance for Stable and Sharp Normal 中文标题&#xff1a;StableNormal&#xff1a;减少扩散方差以实现稳定且锐利的法线 简介&#xff1a;本文介绍了一种创新解决方案&#xff0c;旨在优化单目彩色输入&#xff08;包括静态图片与动态…

最新自助下单彩虹云商城系统源码,含小储云商城模板免授权

最新彩虹商城源码,含小储云商城模板免授权&#xff0c;试用了一下还行&#xff0c;具体的大家可以看看 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89405387 更多资源下载&#xff1a;关注我。

通过混合栅极技术改善p-GaN功率HEMTs的ESD性能

来源&#xff1a;Improved Gate ESD Behaviors of p-GaN PowerHEMTs by Hybrid Gate Technology&#xff08;ISPSD 24年&#xff09; 摘要 本工作中&#xff0c;首次证明了混合栅极技术在不增加额外面积和寄生效应的前提下&#xff0c;能有效提升p-GaN HEMTs的栅极静电放电(E…

2024广东省职业技能大赛云计算赛项实战——构建CICD

构建CI/CD 前言 题目如下&#xff1a; 构建CI/CD 编写流水线脚本.gitlab-ci.yml触发自动构建&#xff0c;具体要求如下&#xff1a; &#xff08;1&#xff09;基于镜像maven:3.6-jdk-8构建项目的drone分支&#xff1b; &#xff08;2&#xff09;构建镜像的名称&#xff1a…

浅浅谈谈如何利用Javase+多线程+计算机网络的知识做一个爬CSDN阅读量总访问量的程序

目录 我们发现csdn的文章 首先为了印证我们的想法 我们用postman往csdn我们任意一篇文章发起post请求 发送请求 ​编辑获得响应结果 我们发现我们的阅读量上涨 PostRequestSender类 但是我们经过测试发现 定义一个字符串数组 把URL放进去 然后延迟启动 在线程池里面…

SaaS架构Client/Server应用的实验室LIS系统源码,服务可拆分,功能易扩展

LIS系统&#xff0c;即实验室&#xff08;检验科&#xff09;信息系统&#xff0c;它是医院信息管理的重要组成部分之一&#xff0c;LIS系统采用了智能辅助功能来处理大信息量的检验工作&#xff0c;即LIS系统不仅是自动接收检验数据&#xff0c;打印检验报告&#xff0c;系统保…

Jboss通过未授权/弱口令进入后台上传webshell

目录 Jboss介绍 CVE-2007-1036 漏洞产生的原因 利用原理 访问页面 写入webshell 未授权访问后上传webshell 这一篇学习通过参考大佬的好文章学习Jboss的弱口令/未授权漏洞进入后台Getshell Jboss介绍 JBoss是一个基于J2EE的开放源代码应用服务器&#xff0c;代码遵循L…

《梦醒蝶飞:释放Excel函数与公式的力量》6.2 TIME函数

6.2 TIME函数 1&#xff09; TIME函数概述 TIME函数是Excel中用于根据指定的小时、分钟和秒返回时间值的内置函数。这个时间值是一个从0&#xff08;12:00 AM&#xff09;开始的序列数&#xff0c;其中一天的每个小时等于1/24&#xff0c;每分钟等于1/(24*60)&#xff0c;每秒…

【系统架构设计师】六、信息系统基础知识(电子政务|企业信息化|电子商务|信息化战略体系)

目录 一、电子政务EG 1.1 电子政务的内容 1.2 电子政务的主要特征 二、企业信息化EI 2.1 企业信息化实现 2.2 企业信息化方法 三、电子商务EC 四、信息化战略体系 五、客户关系CRM 5.1 CRM的功能 5.2 CRM解决方案具备的要素 5.3 CRM的实现过程 六、供应链管理SCM 七…

Flutter TIM 项目实现

目录 1. 服务端API 1.1 生成签名 1.1.1 步骤 第一步:获取签名算法 第二步:查看函数输入输出 第三步:nodejs 实现功能 1.1.2 验证签名 小结 1.2 Rest API 调用 1.2.1 签名介绍 1.2.2 腾讯接口 生成管理员 administrator 签名 包装一个 post 请求函数 查询账号 …

2734. 执行子串操作后的字典序最小字符串(Rust单百算法)

题目 给你一个仅由小写英文字母组成的字符串 s 。在一步操作中&#xff0c;你可以完成以下行为&#xff1a; 选择 s 的任一非空子字符串&#xff0c;可能是整个字符串&#xff0c;接着将字符串中的每一个字符替换为英文字母表中的前一个字符。例如&#xff0c;‘b’ 用 ‘a’…

华为OD机试 - 石头剪刀布游戏(Java 2024 D卷 200分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测…

酷开科技丨酷开系统大屏购物 打造沉浸式购物体验

在这个信息化的时代&#xff0c;购物已经不仅仅局限于传统的线下店铺&#xff0c;线上购物逐渐成为了我们生活中重要的一部分。而大屏购物作为线上购物的一种形式&#xff0c;更是凭借其独特的优势和实用的技巧&#xff0c;成为了消费者们的新宠。随着科技的进步和消费者需求的…

lumbda常用操作

文章目录 lumbda的常用操作将List<String>转List<Integer>filter 过滤max 和min将List<Object>转为Map将List<Object>转为Map&#xff08;重复key&#xff09;将List<Object>转为Map&#xff08;指定Map类型&#xff09; lumbda的常用操作 将Li…