算法【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;}
}

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

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

相关文章

使用VNC-viewer对树莓派5 远程连接桌面—详细记录笔记版

树莓派5 的远程桌面连接&#x1f680; 在完成了对树莓派镜像的安装&#xff0c;以及点亮了屏幕之后&#xff0c;接下来就是为开发做一些准备&#xff0c;就是配置树莓派5的远程的桌面的操作&#xff0c;因为如果不这样的话&#xff0c;我在PC上和树莓派系统上分别进行作业的时候…

CSS 布局

CSS 页面布局技术允许我们拾取网页中的元素&#xff0c;并且控制它们相对正常布局流、周边元素、父容器或者主视口/窗口的位置。布局有一下几种 正常布局流display属性弹性盒子网格浮动定位CSS 表格布局多列布局 每种布局都有它们的用途&#xff0c;各有优缺点&#xff0c;相…

实时监控Windows服务器:使用Prometheus和Grafana的终极方案

1. 下载并安装 Prometheus 下载 Prometheus&#xff1a; 访问 Prometheus 下载页面。下载适用于 Windows 的压缩包&#xff08;.zip 文件&#xff09;。prometheus-2.53.2.windows-amd64.zip 下载其中一个就行 安装 Prometheus&#xff1a; 解压下载的压缩包到你选择的目录&a…

Centos7主机带宽限速

需求&#xff1a;最近有两个主机经常把带宽打满。咨询了阿里云无法对内网网卡做限制。这边想使用linux默认的TC工具。 限速之前测试带宽。这时带宽有 168.4MB/s。 ]# scp filebeat-8.8.2-x86_64.rpm 172.116.47.54:/root/100% 26MB 168.4MB/s 00:00 1. 限制出站&#xff0…

Python编码系列—掌握Python Web开发:Flask与FastAPI实战应用

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

ubuntu 24.04 安装 Nvidia 显卡驱动 + CUDA + cuDNN,配置 AI 深度学习训练环境,简单易懂,一看就会!

ubuntu 24.04 安装 Nvidia 显卡驱动 CUDA cuDNN&#xff0c;配置 AI 深度学习训练环境&#xff0c;简单易懂&#xff0c;一看就会&#xff01; 1.查看本机显卡型号 lspci | grep -i nvidia输出如下&#xff1a; 01:00.0 3D controller: NVIDIA Corporation GM108M [GeForc…

C语言常用的内存函数

在上一篇博客中我为大家分享了一些常用的字符串函数&#xff0c;以及它们的用法和模拟实现。通过字符串函数中的strcpy&#xff0c;我们能够做到将一个字符串中的内容拷贝到另一个字符串上&#xff0c;可如果有一天我们想把一个整型数组中的内容拷贝到另一个整型数组中呢&#…

KV存储之ETCD

ETCD 是一种分布式键值存储系统&#xff0c;主要用于分布式系统中的配置管理、服务发现和分布式协调。它由 CoreOS 团队开发&#xff0c;现在是 CNCF&#xff08;云原生计算基金会&#xff09;托管的一个开源项目。ETCD 在设计时非常注重一致性、可用性和性能&#xff0c;通常被…

Eclipse的使用配置教程:必要设置、创建工程及可能遇到的问题(很详细,很全面,能解决90%的问题)

Eclipse的使用配置&#xff1a; Ⅰ、Eclipse 的必要配置&#xff1a;1、Eclipse 的安装&#xff1a;其一、将 Eclipse 解压或安装到没有中文且没有空格的路径下。其二、拿到 eclipse.exe 文件&#xff0c;傻瓜式安装即可; 2、设置工作空间(workspace)&#xff1a;其一、首次启动…

C程序设计——基本变量类型(指针杂谈)

瞎聊 本文后面的内容&#xff0c;可以暂时看不懂&#xff0c;以后如果从事这一行&#xff0c;慢慢会理解&#xff0c;但是这句话要记住&#xff1a;如果 piInt 是一个指向整型的指针变量&#xff0c;那么 *piInt 就是一个整型变量&#xff1b;类似的&#xff0c;如果pcChar是…

原生微信小程序笔记完整总结4.0

&#x1f939;‍♀️潜意识起点&#xff1a;个人主页 &#x1f399;座右铭&#xff1a;得之坦然&#xff0c;失之淡然。 &#x1f48e;擅长领域&#xff1a;大前端 是的&#xff0c;我需要您的&#xff1a; &#x1f9e1;点赞❤️关注&#x1f499;收藏&#x1f49b; 是我…

【MySQL】事务管理

【MySQL】事务管理 什么是事务为什么要有事务事务的版本支持事务的提交方式事务的常见操作事务的隔离级别如何理解隔离性隔离级别隔离级别的设置与查看读未提交【Read Uncommitted】读提交【Read Committed】可重复读【Repeatable Read】串行化【serializable】一致性(Consiste…

代码随想录算法训练营43期 | Day 14——226.翻转二叉树、101. 对称二叉树、104.二叉树的最大深度、二叉树最小深度

代码随想录算法训练营 226.翻转二叉树101. 对称二叉树递归法 104.二叉树的最大深度二叉树最小深度 226.翻转二叉树 leetcode链接 思路&#xff1a; 递归三部曲&#xff1a; 确定递归函数的参数和返回值确定终止条件确定单层递归的逻辑 递归法 TreeNode* invertTreeNode(Tree…

并发系统的 CSP+PAT 形式化建模与验证方法(以Kafka系统为例)

消息队列中间件是分布式系统的重要组成部分。它允许应用程序仅关注数据本身&#xff0c;而无需关心数据传输的具体细节。这一特性有效解决了消息异步传输、应用程序解耦以及流量削峰等问题。Kafka是一个开源的分布式消息系统&#xff0c;它基于发布-订阅模型构建。Kafka具有低延…

Unity使用代码生成ScriptableObject数据并赋值之后,重启数据就没有啦!

2024年8月14日早&#xff0c;因数据持续化存储&#xff0c;重启电脑后数据会丢失&#xff0c;而我找不到原因被领导质疑了&#xff0c;故写一片博客记录这个错误。 省流 使用在编辑器的play模式中为ScriptableObject赋值之后&#xff0c;需要使用 #if UNITY_EDITORUnityEdit…

Codeforces Round 495 (Div. 2) F. Sonya and Bitwise OR(线段树)

原题链接&#xff1a;F. Sonya and Bitwise OR 题目大意&#xff1a; 给出一个长度为 n n n 的数组 a a a&#xff0c;并给出 m m m 次询问以及一个数字 x x x。 每个询问形式如下给出&#xff1a; 1 1 1 i i i y y y &#xff1a;将 a i a_{i} ai​ 位置的值更改为 y…

数据库分库分表的介绍

为什么要分库分表 把存于一个库的数据分散到多个库中&#xff0c;把存于一个表的数据分散到多个表中。如果说读写分离是为了分散数据库读写操作压力&#xff0c;分库分表就是为了分散存储压力&#xff0c;一般情况下&#xff0c;单表数据量到达千万级别&#xff0c;就可以考虑…

基于飞腾平台的Hbase的安装配置

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

支持S/MIME证书的邮件客户端有哪些?

S/MIME证书&#xff0c;也叫做邮件安全证书&#xff0c;支持安全/多用途互联网邮件扩展协议&#xff08;S/MIME协议&#xff09;&#xff0c;是通过加密和数字签名来确保电子邮件的安全性、保密性和完整性的数字证书。GDPR、HIPAA、FDA等多个行业都要求邮件发送方在发送邮件时对…

基于R语言遥感随机森林建模与空间预测;遥感数据处理与特征提取;数据分析与可视化

目录 第一章 理论基础与数据准备【夯实基础】 第二章 随机森林建模与预测【讲解实践】 第三章 实践案例与项目 更多应用 随机森林作为一种集成学习方法&#xff0c;在处理复杂数据分析任务中特别是遥感数据分析中表现出色。通过构建大量的决策树并引入随机性&#xff0c;随…