当前位置: 首页 > news >正文

算法【Java】—— 双指针算法

双指针算法

常见的双指针有对撞指针,快慢指针以及前后指针(这个前后指针是指两个指针都是从从一个方向出发,去往另一个方法,也可以认为是小学学习过的两车并行,我也会叫做同向指针),在前后指针这里还有一个经典的算法叫做滑动窗口,滑动窗口会在下一篇算法文章中提到

对撞指针可以叫做左右指针,就是一个指针在最左端,另一个指针在最右端,然后两个指针开始向中间逼近。

快慢指针:相信大家在链表的时候就已经学到过,就是一个指针每次走一步(这就是慢指针),另一个指针每次走两步(这是快指针)。一般快慢指针是用来处理处理环形链表或数组。

前后指针:在不涉及到滑动窗口的使用的时候,我们一般会用于数组的分区

如果大家学过排序,想必对双指针应该十分了解,正好在学习双指针算法的时候,可以回顾一下排序内容。

题目实战

下面题目讨论的时间复杂度没有进行化简,目的是让大家更好地感受算法对性能的优化。

移动零

https://leetcode.cn/problems/move-zeroes/description/

在这里插入图片描述

解法:前后指针
题目要求我们将数组前面的零移动到后面,将非零的元素按照原本的相对位置存放到数组的前面。
这时候这个数组显而易见地被分成了两个部分,一个是非零区域,一个是零区域

我们可以使用前后指针法,第一个指针从下标0开始遍历数组,第二个指针初始值设置为-1,当第一个指针遇到非零元素时,第二个指针向后移动一步,然后交换两个指针所对应的数组的元素,否则第二个指针原地不动。

在这里插入图片描述

通过前后指针,我们可以将数组分成上述的区域,这就是为什么前后指针一般用于数组的分区。

class Solution {public void moveZeroes(int[] nums) {int len = nums.length;int j = -1;for(int i = 0; i < len; i++) {if(nums[i] != 0) {j++;int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}}
}

如果可以使用额外的空间的话,还是使用两个指针,一个指针指向一个数组,时间复杂度为O(2N),空间复杂度为O(N),但是直接使用双指针算法,时间复杂度O(2N),空间复杂度为O(1)

快乐数

https://leetcode.cn/problems/happy-number/

在这里插入图片描述

解法:快慢指针
在快乐数的循环中,我们知道循环最后的结果要么是无限循环,要么是 1,那无限循环是为什么?

以 n = 2 为例:2 ^ 2 = 4,4 ^ 2 = 16,1 ^ 2 + 6 ^ 2 = 37,3 ^ 2 + 7 ^ 2 = 58,5 ^ 2 + 8 ^ 2 = 89,8 ^ 2 + 9 ^ 2 = 145, 1 ^ 2 + 4 ^ 2 + 5 ^ 2 = 42,4 ^ 2 + 2 ^ 2 = 20,2 ^ 2 + 0 = 2,最后你会发现这个数它又回去了,也就是意味着这个循环形成了一个环

最后循环的结果还有一种可能就是出现1,如果出现 1 的时候,我们不停止循环的话,那么这个循环将会一直得到1,这也是一个环。

相信大家到这里想到了使用快慢指针,当两个指针相遇时,如果指针对应的是1 的话,那么就是快乐数,否则就不是快乐数。

class Solution {public boolean isHappy(int n) {int slow = n;int fast = sum(sum(n));while(slow != fast) {slow = sum(slow);fast = sum(sum(fast));}if(slow == 1) {return true;} else {return false;}}private int sum(int n) {int sum = 0;while(n != 0) {sum += (n % 10) * (n % 10);n /= 10;}return sum;}
}

盛最多水的容器

https://leetcode.cn/problems/container-with-most-water/description/

在这里插入图片描述

解法:对撞指针

分析题意:数组的每一个元素是该下标的高,而容器的底长为两个下标之间的差值,题目要求我们找到容器的最大值,这个数值等于 高 x 底长

我们使用对撞指针,一个在最左端,一个在最右端,然后两个指针开始向中间遍历,首先由于两个指针是向中间靠拢,所以对应的宽度就会减少,指针移动的目的是为了获取容器的最大值,所以,我们现在要思考指针如何移动?

因为容器的高度是由最小的高度决定的,所以这时候,我们可以从两个指针获取到最小的元素,我们让对应最小的高度的指针向中间移动,看看能不能找到更大的高度,以此来进行遍历,然后通过 Math.max 这个函数就可以获取到最大值。

class Solution {public int maxArea(int[] height) {int left = 0;int right = height.length - 1;int maxV = Math.min(height[left],height[right]) * (right - left);while(left < right) {int index = (height[left] > height[right] ? right : left);if(index == left) {left++;} else {right--;}int tmp = Math.min(height[left],height[right]) * (right - left);maxV = Math.max(maxV,tmp);}return maxV;        }
}

这道题的对撞指针算法的时间复杂度为O(N) ,性能十分地好

两数之和

https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/

在这里插入图片描述

解法:排序 + 双指针

由于我们只要返回两个数(两数之和为 target 即可)。如果我们直接使用两个指针的话,就是使用暴力枚举,使用了两层循环,实践复杂度为O(N^2),但是这里是算法篇目,我们应该尽量优化我们的算法,将算法的效率提高。

如果数组本身就是有序的话,我们再这个基础上使用双指针就会好写很多,因为就三种情况,要么两数之和小于taregt ,两数之和如果等于 tareget 的话,直接返回即可,如果两个数大于 target 。

也就是我们要处理指针的移动就是大于和小于的情况,如果是大于的话,我们可以将指针左移减小两数之和,如果是小于的话,可以将指针右移扩大两数之和,那这就意味着我们要使用的是对撞指针。

class Solution {public int[] twoSum(int[] price, int target) {Arrays.sort(price);int left = 0;int right = price.length - 1;while(left < right) {if(price[left] + price[right] == target) {break;} else if(price[left] + price[right] > target) {right--;} else {left++;}}int[] ans = {price[left],price[right]};return ans;}
}

时间复杂度分析,使用Arrays.sort ,这个底层是快速排序,时间复杂度为O(N * logN),对撞指针时间复杂度为 O(N), 整体时间复杂度为 O(N + N * logN),比暴力枚举好多了。

三数之和

https://leetcode.cn/problems/3sum/

在这里插入图片描述

解法:双指针

在做这道题目的时候,我们一开始想到的就是直接暴力求解,使用三个循环,时间复杂度为 O(N ^ 3) ,作为一个算法题,我们需要优化它的性能,我们可以使用双指针来代替两个循环,这样最多只需要遍历 2N 个元素,加上一个循环,时间复杂度就可以变为 O(N ^ 2)

我们可以借助两数之和这道题目的算法思路,通过对撞指针找到 两数之和为 taregt ,而target 可以由 0 - 第三个数字获得,使用这个思路的时候,需要先对数组排好序。

这里有一个方法 能将一串数字转化为 链表 —— Arrays.asList(…),这样就不用这么麻烦添加元素到链表上了

这里要避免重复的三元组的出现,再取完三元组后,双指针各自向中间靠拢一步,然后进行去重操作,避免和上回的元素相同。
在每次执行完双指针算法的时候,可以再对 最外层循环的变量先 ++ ,再去重,因为这个元素的三元组已经找过了。

为什么去重之后,三元组就不会重复?
因为数组是有序的,双指针是向中间靠拢的,左指针所对应的数值一定的大于上回的数值,右指针对应的数值一定的小于上回的数值,并且左指针一定小于右指针,所以右指针不可能去到左指针上回的数值,这就保证有一个数字是一定不重复的,也就保证三元组的不重复性

经过双指针的优化,时间复杂度为O(N^2 + N * logN)

class Solution {public List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);List<List<Integer>> list = new ArrayList<>();int len = nums.length;for(int i = 0; i < len - 2;) {int target = 0 - nums[i];for(int left = i + 1, right = len - 1; left < right;) {if(nums[left] + nums[right] == target) {List<Integer> l = new ArrayList<>(Arrays.asList(nums[i],nums[left],nums[right]));list.add(l);left++;right--;while(left < right && nums[right+1] == nums[right]) {right--;}while(left < right && nums[left-1] == nums[left]) {left++;}} else if (nums[left] + nums[right] > target) {right--;} else {left++;}}//去重i++;while(i < len && nums[i-1] == nums[i]) {i++;}}return list;}
}

相关文章:

算法【Java】—— 双指针算法

双指针算法 常见的双指针有对撞指针&#xff0c;快慢指针以及前后指针&#xff08;这个前后指针是指两个指针都是从从一个方向出发&#xff0c;去往另一个方法&#xff0c;也可以认为是小学学习过的两车并行&#xff0c;我也会叫做同向指针&#xff09;&#xff0c;在前后指针…...

【Python快速入门和实践013】Python常用脚本-目标检测之按照类别数量划分数据集

一、功能介绍 这段代码实现了从给定的图像和标签文件夹中分割数据集为训练集、验证集和测试集的功能。以下是代码功能的总结&#xff1a; 创建目标文件夹结构&#xff1a; 在指定的根目录&#xff08;dataset_root&#xff09;下创建images和labels两个文件夹。在这两个文件夹下…...

C++ Primer 总结索引 | 第十八章:用于大型程序的工具

1、大规模应用程序的特殊要求包括&#xff1a; 在独立开发的子系统之间 协同处理错误的能力使用各种库&#xff08;可能包含独立开发的库&#xff09;进行 协同开发的能力对比较复杂的应用 概念建模的能力 对应 异常处理、命名空间和多重继承 1、异常处理 1、异常处理机制 …...

Python实现GAN(生成对抗网络)图像修复算法

目录 1. GAN简介与图像修复2. PyTorch和CUDA简介3. 数据加载与预处理3.1 安装依赖3.2 数据加载3.3 数据遮挡4. 构建GAN图像修复模型4.1 生成器4.2 判别器5. 训练GAN模型5.1 损失函数与优化器5.2 训练循环6. 测7. 实现GUI进行图像修复8. 总结与扩展扩展方向:1. GAN简介与图像修…...

java语言中的websocket

你好&#xff01;我是TensGPT&#xff0c;一个由TensGPT团队开发的AI助手。我可以帮助你了解和使用Java语言中的WebSocket。如果你有任何问题或需要示例代码&#xff0c;请告诉我。 ### 什么是WebSocket&#xff1f; WebSocket是一种在单个TCP连接上进行全双工通信的协议。它被…...

ASP.NET在线交流论坛管理系统

ASP.NET在线交流论坛管理系统 说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net架构和sql server数据库 用户功能有个人信息管理 帖了信息管理 意见反馈信息管理 点赞管理 收藏管理 后台管理员可以进行用户管理 …...

【Kubernetes】身份认证与鉴权

一&#xff0c;认证 所有 Kubernetes 集群有两类用户&#xff1a;由Kubernetes管理的ServiceAccounts(服务账户)和(Users Accounts)普通账户。 两种账户的区别&#xff1a; 普通帐户是针对(人)用户的&#xff0c;服务账户针对Pod进程普通帐户是全局性。在集群所有namespaces…...

数据集与数据库:有什么区别?

数据集和数据库是我们在处理数据时经常听到的两个常用词。虽然它们听起来很相似&#xff0c;但它们具有不同的特征并用于不同的用途。本文深入探讨数据集和数据库之间的主要区别&#xff0c;探索了它们的结构、数据类型和各种其他功能&#xff0c;以帮助您做出明智的决定&#…...

BurpSuite

如果只能用一个Web渗透工具&#xff0c;我选BurpSuite。 Web应用程序&#xff08;Web Application&#xff09; 不同于传统的静态网站所有程序的特点是接收、处理用户输入并返回结果服务器端是个程序&#xff0c;需要程序代码实现业务功能&#xff08;java、php、asp.nse&…...

NetApp数据恢复—NetApp存储误删除文件如何恢复数据?

NetApp数据恢复环境&故障&#xff1a; 某公司一台NetApp存储&#xff0c;该存储中有24块磁盘。 工作人员误删除了NetApp存储中一个文件夹&#xff0c;文件夹中有非常重要的数据。 数据恢复工程师在现场对该存储进行了初检。虽然这个文件夹被删除很长时间&#xff0c;但是根…...

基于springboot的医药管理系统

TOC springboot194基于springboot的医药管理系统 绪论 1.1 选题背景 当人们发现随着生产规模的不断扩大&#xff0c;人为计算方面才是一个巨大的短板&#xff0c;所以发明了各种计算设备&#xff0c;从结绳记事&#xff0c;到算筹&#xff0c;以及算盘&#xff0c;到如今的…...

Android中的EventBus的用法

1. EventBus简介 EventBus是一个优化了的事件发布/订阅模式实现的库&#xff0c;常用于Android程序组件间的通信。它可以简化不同组件之间的通信工作&#xff0c;避免复杂和耦合的依赖关系。EventBus通过事件驱动来降低代码耦合度&#xff0c;提高开发效率和代码清晰性。 2. …...

梧桐数据库(WuTongDB):数据库在数据处理中是如何利用缓存机制的

数据库在数据处理中利用缓存机制主要是为了提高数据访问速度和系统性能。缓存机制通过将频繁访问的数据存储在内存中&#xff0c;减少了对磁盘I/O操作的需求&#xff0c;从而提高了数据查询的效率。以下是数据库利用缓存机制的一些主要方式&#xff1a; 1. 查询缓存&#xff0…...

C语言-数据类型

在x64编译器平台下&#xff0c;C语言数据类型的取值范围主要取决于数据类型的大小&#xff08;即字节数&#xff09;以及它们是有符号的还是无符号的。以下是根据常见实现总结的x64平台下C语言数据类型的取值范围&#xff1a; 整数类型 浮点类型 指针类型 在x64编译器平台下…...

左值引用、右值引用、移动构造

1、为啥使用引用&#xff1f; // An highlighted block void function(string str) {... ... }看上面这段代码&#xff0c;如果不采用引用的方法&#xff0c;那么在函数被调用的时候&#xff0c;编译器会有一个参数赋值的过程&#xff0c;这就导致了内存和效率的浪费。 // An…...

tekton通过ceph挂载node_modules的时候报错failed to execute command: copying dir: symlink

分析&#xff1a; 如果ceph的mountPath和workingDir路径一致的话&#xff0c;就会报错。 解决&#xff1a;node_modules挂载到/workspace下&#xff0c;workingDir的代码mv到/workspace下进行构建。...

Xil_DCacheFlushRange的用法

概述&#xff1a; 当使用Zynq的PS (Processing System) 与PL (Programmable Logic) 进行通信时&#xff0c;特别是涉及到高速数据传输时&#xff0c;可能会遇到缓存一致性问题。这是因为处理器系统通常具有缓存机制来加快对常用数据的访问速度&#xff0c;但在某些情况下&…...

k8s使用subpathexpr和hostpath分pod名字持久化日志

在k8s中&#xff0c;服务日志除了标准输出&#xff0c;还有写入日志文件&#xff0c;若要对这些日志文件进行持久化存储&#xff0c;无论是通过网络文件存储还是hostpath&#xff0c;都会面临一个问题&#xff0c;多个pod会往同一个存储目录的同一个文件进行写入&#xff0c;导…...

FChen的408学习日记--三次握手和四次握手

一、三次握手 在建立连接的过程中&#xff0c;首先SYN1&#xff0c;随机发送sqex。服务器接受后要反过来对客户端发送连接请求&#xff0c;SYN1&#xff0c;随机发送sqey&#xff0c;ackx1。然后客户端还要发送连接确认报文&#xff0c;原因如下 例题&#xff1a; 二、四次…...

Unity技巧:轻松实现鼠标悬停文本时的动态变色效果

文章目录 前言一、Text二、TMP_Text二、颜色转换总结 前言 在游戏或应用中&#xff0c;给用户的界面添加一些小的互动效果能让它们更加吸引人。比如&#xff0c;当策划要求你这样做的时候 &#xff0c;当用户将鼠标悬停在文字上时&#xff0c;文字颜色改变&#xff0c;这样的效…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里&#xff0c;Bessie 有一个烤箱&#xff0c;可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...

如何把工业通信协议转换成http websocket

1.现状 工业通信协议多数工作在边缘设备上&#xff0c;比如&#xff1a;PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发&#xff0c;当设备上用的是modbus从站时&#xff0c;采集设备数据需要开发modbus主站&#xff1b;当设备上用的是西门子PN协议时&#xf…...