【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,一经查实,立即删除!

相关文章

Unity使用Fmod的线程安全大坑!

对EventInstance设置回调函数时&#xff0c;回调函数内不能调用协程。否则会秒崩溃&#xff0c;并且不带任何报错&#xff01; 协程在主线程中执行&#xff0c;但是事件回调是异步在音频线程中执行的 Event Callbacks 某些 EVENT_CALLBACK_TYPE 回调会在音频线程中执行,比如 …

Axure 教程 | 雅虎新闻焦点

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

水平拆分的技巧

水平拆分的原则和技巧 水平拆分是一种常见的数据库优化手段&#xff0c;它涉及到将一个大型表根据特定的字段值拆分成多个较小的表。这种做法可以有效地提高查询效率&#xff0c;尤其是在处理大量数据时。以下是一些关于如何根据业务设计字段进行水平拆分的建议&#xff1a; …

马斯克的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;然后登录同一…

从零开始:STM32与W25Q64 Flash存储器的SPI接口全解析

摘要 本文将从基础出发&#xff0c;全面解析STM32微控制器与W25Q64 Flash存储器通过SPI接口的通信。内容包括SPI基础知识、W25Q64特性、硬件连接、SPI接口配置、读写操作及实际代码示例。 1. SPI通信基础 SPI是一种串行通信协议&#xff0c;主要特点包括&#xff1a; 同步通…

2008-2022年 上市公司-环境绩效数据(EP)

环境绩效&#xff08;Environmental Performance&#xff0c;简称EP&#xff09;是衡量组织在环境管理方面成效的重要指标&#xff0c;它体现了组织如何通过有效的环境管理体系来控制环境因素&#xff0c;实现环境方针和目标。以下是对上市公司环境绩效数据的详细介绍&#xff…

图的拓扑排序

图的拓扑排序&#xff08;Topological Sorting&#xff09;是一种线性排序&#xff0c;用于有向无环图&#xff08;Directed Acyclic Graph&#xff0c;DAG&#xff09;。拓扑排序将图中的顶点排成一个线性序列&#xff0c;使得对于每一条有向边 (u, v)&#xff0c;顶点 u 都排…

安全继电器的使用和工作原理

安全继电器的使用和工作原理 安全继电器是一种用于保护人员和设备安全的电气装置&#xff0c;通常用于监控和控制危险或紧急情况下的电气系统。它的主要作用是在检测到故障或危险情况时迅速切断电路&#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;如果你的虚拟机有快照是无…

Python高级编程:自然语言处理基础

Python高级编程:自然语言处理基础 在前几篇文章中,我们探讨了Python的基础语法、面向对象编程、标准库、第三方库、并发编程、异步编程、网络编程与网络爬虫、数据库操作与ORM、数据分析与数据可视化、机器学习基础以及深度学习基础。在这篇文章中,我们将深入探讨Python在自…

使用不同环境的配置文件active profile

在 IntelliJ IDEA 的 Run/Debug Configurations 中&#xff0c;Active profiles 选项通常用于与 Spring Boot 应用程序相关的配置。这是 Spring Boot 特有的一个用来管理不同环境配置的特性&#xff0c;通常用来在开发&#xff08;dev&#xff09;、测试&#xff08;test&#…

Springboot基于Redis的高性能分布式缓存数据库的实现与实例

一、引言 在现代的分布式系统和高并发应用中&#xff0c;缓存机制显得尤为重要。Redis作为一种开源&#xff08;BSD许可&#xff09;的内存键值存储&#xff0c;因其高性能、丰富的数据结构和多样化的应用场景&#xff0c;成为开发者们的首选。在这篇博客中&#xff0c;我们将…

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放进去 然后延迟启动 在线程池里面…