双指针算法习题解答

1.移动零

题目链接:283. 移动零 - 力扣(LeetCode)

题目解析:该题要求将数组中为0的元素全部转移到数组的末尾,同时不能改变非零元素的相对位置。

解题思路:我们可以用变量dest和cur将该数组分为三个区域。如下图 

接着我们可以将数组中为0的元素放在[0,dest]的区域,将数组中非0的元素放在[dest+1,cur-1]的区域,而[cur,n-1]是待处理的区域。等数组中全部处理完之后,就如下图所示。 

实现思路:

我们用cur来遍历数组,如果cur遇到数据为0的元素,则让cur++。如果cur遇到非0的元素,因为要将非0数据放在[dest+1,cur-1]区域,所以,我们先将dest++,然后交换nums[dest]和nums[cur]的值。

代码实现

    public void moveZeroes(int[] nums) {int dest=-1;for(int cur=0;cur<nums.length;cur++){if(nums[cur]!=0){dest++;int tmp=nums[cur];nums[cur]=nums[dest];nums[dest]=tmp;}}}

2.复写零

题目链接:1089. 复写零 - 力扣(LeetCode) 

解题思路:我们可以用一个cur指针和一个dest指针,用cur指针来遍历数组并且用来确定复写的数,用dest来实现复写的操作。

首先,我们想到从前往后复写,但此时我们会发现,当cur遇到0的时候,我们用dest来实现复写的时候,由于0要复写2次,此时,会将cur后面没实现复写的一个数覆盖掉,这样就会漏掉一个数没法实现复写。

所以,我们要从后往前实现复写。

步骤1.首先,我们要找到最后一个复写的数 。

我们也可以用双指针实现找到最后一个复写的数。用cur来遍历数组,我们先让dest指向-1的位置,当cur遇到数据为非0的数,我们让dest向后走两步,如果cur遇到数据为0的数,我们让dest向后走一步,直到dest走到数组的末尾或者大于末尾的位置。

步骤2.其次我们从后向前实现复写的操作,我们也是通过cur来遍历数组,当cur遇到非0的数据,让dest向前走一步,如果cur遇到数据为0的数,我们让dest向前走两步。

但是在此之前,我们要处理一个边界情况,也就是,当我们返现当数组中最后一个要复写的数是0且该数是数组中倒数第二个数的时候,dest会越界。

如下图

最后,我们实现从后向前的复写就行了。

如下图

    public void duplicateZeros(int[] arr) {int cur=0;int dest=-1;int n=arr.length;//寻找最后一个复写的数while(cur<n){if(arr[cur]!=0){dest+=1;}else{dest+=2;}if(dest>=n-1){break;}cur++;}//处理边界情况if(dest>=n){arr[dest-1]=0;dest-=2;cur--;}//实现从后向前复写while(cur>=0){if(arr[cur]!=0){arr[dest--]=arr[cur];}else{arr[dest--]=0;arr[dest--]=0;}cur--;}}

3.快乐数

题目链接:202. 快乐数 - 力扣(LeetCode) 

解题思路:

通过题目例子,我们来手动演示以下判断该数是否为快乐数的的过程。

通过手动演示,我们发现在判断数据是否为快乐数的过程中,我们发现数据的变化会成为一个环。也就是说,在这个过程中,数据迟早会有一次演变成环中的数。

我们将上图抽象成如下图的情况

当我们抽象成右边图的时候,这就更我们在学习链表中求链表是否存在环的情况很相似。不过在这里,我们不是判断链表中是否有环,而是判断环中的数据是否为1。

前面,我们在解决判断链表中是否有环的时候,用了快慢指针,这道题,我们也可以用快慢指针。

我们每次让slow一次变化一次,让fast一次变换两次。(这里的变换是指在判断是否为快乐数的过程中,数据的变换)。

以一开始的19为例,slow变化一次就是82,fast变化两次就是68。

    public int bitSum(int n){int sum=0;while(n!=0){int tmp=n%10;sum+=tmp*tmp;n=n/10;}return sum;}public boolean isHappy(int n) {int slow=n;//由于要进入循环,我们一开始就要将fast放在slow后面int fast=bitSum(n);while(slow!=fast){slow=bitSum(slow);//让slow往后走一步fast=bitSum(bitSum(fast));//fast往后走两步}return slow==1;}

拓展:这时候,有人就会疑惑又没有可能一个数据在变换的过程中,会一直变化下去,不会成环呢?

答案就是,数据在进行变化的过程中,变化的数据一定会成环。 

证明:

这就涉及到一个鸽巢原理。

鸽巢原理:当有n+1只格子和n个鸽巢的时候,必定会有一个鸽巢的鸽子数量大于等于1,也就是总会有至少两只鸽子共享一个窝。

此时,我们将题目的数据观察题目的数据范围,如下图

n的最大值为2147483647,相同位数的最大值为9999999999,推出数据变化的范围在[1,810]之间。

所以,我们可以达到,数据在变化的过程中,变化的数肯定在[1,810]之间。

我们就算他变化无数次,变化的数据还是在[1,810]之间,所以可以得出变化的过程中,数据一定会成环。 

4.盛水最多的容器

题目链接:11. 盛最多水的容器 - 力扣(LeetCode)

解法思路:对撞指针和单调性

 我们可以设一个指针left指向数组中的第一个元素,指针right指向最后一个元素,(right-left)的值就是宽,Math(height[left],height[right])就是高,接着根据高和宽求面积。 

由于题目要求是求最大的面积,所以,我们要改变left和right的指向的位置,分别求处不同情况下的面积,然后再这些面积中求一个最大值即可。

我们以何种方式来改变left合right指向的位置呢?

我们一次只变一个,要么让left++,要么right--,我们知道面积=宽*高,而宽=right-left,而再left++的过程中或者是right--的过程中,宽度的值一定是减小的。

所以,我们让height[left]和height[right]中数值较小的值移动。

为什么呢?这就涉及到单调性。如下图解释

我们让指向值较小的指针位置变换,实质上是放弃这个较小数的枚举,因为以这个数枚举的面积都是在单调减小的,所以,我们就让数值较小的指针移动。

代码实现

    public int maxArea(int[] height) {int n=height.length;int left=0;int right=n-1;int v=0;while(left<right){int tmp=Math.min(height[left],height[right])*(right-left);v=Math.max(v,tmp);if(height[left]<height[right]){left++;}else{right--;}}return v;}

5.有效三角型个数

题目链接:611. 有效三角形的个数 - 力扣(LeetCode) 

解法思路:双指针和单调性

首先,我们先普及一个判断三角型的知识点,如果我们知道a<b<c,那么我们只需判断a+b>c成立,就可以判断这三条边可以组成三角形。

所以,我们可以先给数组进行排序,这是一步优化。

接着,我们就可以先固定一个最大数,因为数组排过序,是一个有序数组,那么这个最大数的左边的数是肯定都比这个最大数小的。接着建立如下图的指针位置

 

这时,我们就来判断a,b,c能否组成三角形。

如果此时a,b,c三条边能组成三角形,那么此时以9为b边,10为c边,能够成三角形的个数就为right-left 个,因为数组是一个有序的数组,那么(left,right)区间里的数都是大于a的数,那么此时就不用通过枚举b边为9的情况下,能组成三角形的情况了。

如下图

遇到这种情况,我们就让right - -就行了 

此时,会遇到第二种情况,此时nums[left]+nums[right]<nums[i],此时我们直接让left++就行了。因为此时(left,right)区间都是小于nums[right]的数了,所以我们此时就不必要以nums[left]为基准,去枚举了,此时,我们直接将此时的2去掉,及让left++就行了。

知道left和right指针相遇,我们在更换最大边,循环以上步骤。

代码实现:

    public int triangleNumber(int[] nums) {Arrays.sort(nums);//固定最大数int ret=0,n=nums.length;for(int i=n-1;i>=2;i--){//固定最大边int left=0;int right=i-1;while(left<right){if(nums[left]+nums[right]>nums[i]){ret+=right-left;right--;}else{left++;}}}return ret;}

6.和为s的两个数字 

题目链接:LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode) 

 解法一:暴力枚举法,但是当数组中的数据太多时,会超时。

    public int[] twoSum(int[] nums, int target) {int n=nums.length;for(int i=0;i<n;i++){for(int j=i+1;j<n;j++){if(nums[i]+nums[j]==target){return new int[]{nums[i],nums[j]};}}}return new int[]{-1,-1};}

解法二:双指针

此时,我们注意题目中的数组是一个有序数组,所以此题我们可以利用数组的单调性和双指针进行优化。

我们分别设置一个left指针和一个right指针,先让left指向数组中的第一个数据,让right指向数组的最后一个数据,然后我们根据left和right指向值的和与target进行比较,不同的情况,让不同指针移动。如下图

此时,left和right指针在移动的过程中会遇到三种情况。

我们先假设left和right指向的值的和为sum。

第一种情况 :sum<target,如果是sum<target的情况,此时因为数组是一个有序数组,而left指向的值是数组中最小的值,right指向的值是数组中最大的值,最小值与最大值的和还是小于target的值,那么此时left指向的值与[left+1,right]区间的任何一个值相加都是小于target的,所以我们就可以大胆得放弃left指向值得枚举,即让left++。

第二种情况:sum>target,如果是sum>target的情况,最小值与最大值的和都大于target了,那么此时right指向的值与[left,right-1]区间的任何一个值相加都是大于target的,所以此时,我们可以大胆的放弃此时right指向值得枚举情况。

第三种情况:sum==target,这种情况直接返回left和right指向的值就行了。

代码实现:

    public int[] twoSum(int[] price, int target) {int n=price.length;int left=0;int right=n-1;while(left<right){if(price[left]+price[right]>target){right--;}else if(price[left]+price[right]<target){left++;}else{return new int[]{price[left],price[right]};}}return new int[]{-1,-1};}

7.三数之和  

题目链接:15. 三数之和 - 力扣(LeetCode) 

 

题目解析:求数组中三个数的和为0,并且要求三个数是数组下表不同的数,并且结果集中的一个子集不能重复出现,子集中的数据顺序也不做要求。

解法一:双指针法

求三个数的和为0,我们也可以用求两数之和的方法来解决该问题。

无非我们先固定一个数,去求另外两个数之和为固定数的相反数就行了。 求两数之和和第6题的一摸一样。

不过该题我们要去处理几个细节问题。

细节一:去重

        当left和right指针遇到相同元素之后,我们要跳过该元素。

        当i也遇到重复元素时,i也要跳过相同的元素。

细节二:不漏

        当i,left和right遇到一个符合情况的三元组时,我们继续让left++,right--。

为了方便去重,我们可以先对数组排序。

代码实现:

    public List<List<Integer>> threeSum(int[] nums) {int n=nums.length;Arrays.sort(nums);//对数组排序List<List<Integer>> ret=new ArrayList<>();for(int i=0;i<n;){if(nums[i]>0) break;//小优化,当nums[i]>0时,就可以跳出循环int target=-nums[i];int left=i+1;int right=n-1;while(left<right){int sum=nums[left]+nums[right];if(sum>target) right--;else if(sum<target) left++;else{ret.add(Arrays.asList(nums[i],nums[left],nums[right]));left++;right--;while(left<right&&nums[left]==nums[left-1]) left++;//去重while(left<right&&nums[right+1]==nums[right]) right--;//去重}}i++;while(i<n&&nums[i]==nums[i-1]) i++;//去重}return ret;}

小细节:我遇到的错误 

 

解法二:暴力枚举法

我们可以 通过排序+枚举+set去重,不过会超时。

class Solution {public List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);List<List<Integer>> ret = new ArrayList<>();Set<List<Integer>> set = new HashSet<>();int len = nums.length;for(int i = 0; i < len; i++) {for(int j = i+1; j < len; j++) {for(int k = j+1; k < len; k++) {if(nums[i] + nums[j] + nums[k] == 0) {set.add(Arrays.asList(nums[i],nums[j],nums[k]));}}}}ret.addAll(set);return ret;}
}

 8.四数之和

题目链接:18. 四数之和 - 力扣(LeetCode)

解题思路:思路和三数之和的思路差不多,不过有个例子时会超出int的最大值,所以需要我们用long来存储。

代码实现:

    public List<List<Integer>> fourSum(int[] nums, int target) {Arrays.sort(nums);int n=nums.length;List<List<Integer>> ret=new ArrayList<>();for(int i=0;i<n;){for(int j=i+1;j<n;){long aim=(long)target-nums[i]-nums[j];int left=j+1;int right=n-1;while(left<right){int sum=nums[left]+nums[right];if(sum>aim) right--;else if(sum<aim) left++;else{ret.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));left++;right--;while(left<right&&nums[left-1]==nums[left]) left++;while(left<right&&nums[right+1]==nums[right]) right--;}}j++;while(j<n&&nums[j-1]==nums[j]) j++;}i++;while(i<n&&nums[i-1]==nums[i]) i++;}return ret;   }

 

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

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

相关文章

「Mac畅玩鸿蒙与硬件23」鸿蒙UI组件篇13 - 自定义组件的创建与使用

自定义组件可以帮助开发者实现复用性强、逻辑清晰的界面模块。通过自定义组件&#xff0c;鸿蒙应用能够提高代码的可维护性&#xff0c;并简化复杂布局的构建。本篇将介绍如何创建自定义组件&#xff0c;如何向组件传递数据&#xff0c;以及如何在不同页面间复用这些组件。 关键…

【SpringCloud】Nacos微服务注册中心

微服务的注册中心 注册中心可以说是微服务架构中的"通讯录"&#xff0c;它记录了服务和服务地址的映射关系 。在分布式架构中&#xff0c; 服务会注册到这里&#xff0c;当服务需要调⽤其它服务时&#xff0c;就从这里找到服务的地址&#xff0c;进行调用。 注册中心…

【Go语言】| 第1课:Golang安装+环境配置+Goland下载

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

数据库优化指南:如何将基本功能运用到极致?

一次问题 数据库的归档日志很多&#xff0c;多到那个机器的硬件不足以处理了。查看了一下为什么产生这么多日志。发现其实都是一些不当的使用方式。比如开发人员建立了一个xxxx_temp从这么名字上就应该能猜出来这是要做什么&#xff1f;美其名曰是临时表。 就是导入一批数据&am…

150道MySQL高频面试题,学完吊打面试官--关于索引的五道大厂面试题,跳槽面试很重要

前言 本专栏为150道MySQL大厂高频面试题讲解分析&#xff0c;这些面试题都是通过MySQL8.0官方文档和阿里巴巴官方手册还有一些大厂面试官提供的资料。 MySQL应用广泛&#xff0c;在多个开发语言中都处于重要地位&#xff0c;所以最好都要掌握MySQL的精华面试题&#xff0c;这也…

自攻螺钉的世纪演变:探索关键设计与应用

自攻螺钉作为现代工业和建筑中的不可或缺的标准部件&#xff0c;经过了超过100年的发展和创新。从1914年最早的铁螺钉设计到今天的自钻自攻螺钉&#xff0c;自攻螺钉的设计不断优化&#xff0c;以适应更复杂的应用需求。本文将回顾自攻螺钉的演变历程&#xff0c;分析其设计原理…

【KMP算法】

目录 BF算法 KMP算法 BF算法 F算法&#xff0c;即暴力(Brute Force)算法&#xff0c;是普通的模式匹配算法&#xff0c;BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配&#xff0c;若相等&#xff0c;则继续比较S的第二个字符和 T的第二个字符&#xf…

快速学习Django框架以开发Web API

简介 Django是一个高级Python Web框架,它鼓励快速开发和简洁实用的设计。由经验丰富的开发者构建,Django可以为你处理大量的Web开发任务,使你能够专注于编写应用的关键组件。Django的模块化设计、可复用性和广泛的社区支持,使其成为开发Web应用和API的理想选择。 在本文中…

论文 | Evaluating the Robustness of Discrete Prompts

论文《Evaluating the Robustness of Discrete Prompts》深入探讨了离散提示&#xff08;Discrete Prompts&#xff09;的鲁棒性&#xff0c;即离散提示在自然语言处理任务中面对不同扰动时的表现。研究特别关注离散提示在自然语言推理&#xff08;NLI&#xff09;任务中的表现…

2024中国国际数字经济博览会:图为科技携明星产品引领数智化潮流

10月24日&#xff0c;全球数智化领域的目光齐聚于中国石家庄正定&#xff0c;一场关于数字经济未来的盛会—2024中国国际数字经济博览会在此拉开帷幕。 云边端算力底座的领航者&#xff0c;图为科技携其明星产品惊艳亮相&#xff0c;期待与您共赴一场数智化的非凡之旅&#xff…

7.2、实验二:被动接口和单播更新

源文件链接&#xff1a; 7.2、实验二&#xff1a;被动接口和单播更新: https://url02.ctfile.com/d/61945102-63671890-6af6ec?p2707 (访问密码: 2707) 一、被动接口 1.介绍 定义: 在路由协议的配置中&#xff0c;一个被动接口指的是一个接口不发送路由更新包的配置方式&a…

4.3 Linux的中断处理流程

点击查看系列文章 》 Interrupt Pipeline系列文章大纲-CSDN博客 原创不易&#xff0c;需要大家多多鼓励&#xff01;您的关注、点赞、收藏就是我的创作动力&#xff01; 4.3 Linux的中断处理流程 先上图&#xff0c;一图胜千言&#xff01; 图中心的蓝色部分&#xff0c;是L…

告别项目混乱,自定义工作项类型驱动项目管理进化

在项目管理中&#xff0c;工作项类型是用于分类和管理项目任务的基本构建块。如瀑布管理和敏捷管理&#xff0c;都有其特定的工作项类型来支持其独特的流程和实践&#xff1a; 敏捷管理的工作项类型 敏捷管理中的工作项类型强调迭代和增量开发&#xff0c;以用户故事为核心&a…

JAVA学习日记(十一) 常用API

一、Math //开平方根 public static double sqrt(double a); //返回结果 //开立方根 public static double cbrt(double a); 水题&#xff1a; public class Main {public static void main(String[] args) {//统计一共有多少个水仙花数 : abca^3b^3c^3abc// aabc/100%10//…

C++ 的异常处理详解

C 的异常处理详解 在编程过程中&#xff0c;错误和异常是不可避免的&#xff0c;合理的异常处理机制能够提高程序的健壮性。在 C 中&#xff0c;异常机制为捕获和处理错误提供了一种结构化的方式。本文将对 C 的异常处理进行详细探讨&#xff0c;包括异常的概念、如何抛出和捕…

IP协议知识点总结

IP协议主要分为三个 1. 地址管理 每个网络上的设备, 要能分配一个唯一的地址 2. 路由选择 小A 给小B 发消息, 具体应该走什么路线 3. 地址管理 IP 地址. 本质上是一个 32 位的整数 通常将, 32 位的整数使用点分十进制来表示, 如 192.168.1.1 一共可以表示 42 亿 9 千万个地址…

秒杀优化(异步秒杀,基于redis-stream实现消息队列)

目录 秒杀优化一&#xff1a;异步秒杀1&#xff1a;思路2&#xff1a;实现 二&#xff1a;redis实现消息队列1&#xff1a;什么是消息队列2&#xff1a;基于list结构实现消息队列3&#xff1a;基于pubsub实现消息队列4&#xff1a;基于stream实现消息队列5&#xff1a;stream的…

小新学习k8s第六天之pod详解

一、资源限制 Pod是k8s中的最小的资源管理组件&#xff0c;pod也是最小化运行容器化应用的资源对象。一个Pod代表着集群中运行的一个进程。k8s中其他大多数组件都是围绕着Pod来进行支撑和扩展Pod功能的&#xff0c;例如&#xff0c;用于管理Pod运行的StatefulSet和Deployment等…

Solana 代币 2022 — Transfer Hook

从零到英雄的 Solana 代币 2022 — Transfer Hook Token 2022 计划引入了几项令人兴奋的扩展&#xff0c;增强了铸造和代币账户的功能。在这些功能中&#xff0c;我个人最喜欢的是Transfer Hook &#xff08;转账钩子&#xff09; 。 想象时间 让我们戴上想象的帽子&#xf…

自定义类型:结构体(一)

一 . 结构体的相关概念 结构体&#xff0c;无需多言&#xff0c;是我们的老朋友了&#xff0c;我们之前就学习过一些有关结构体的知识&#xff0c;今天我们就来正式认识一下这个朋友 结构体属于一种自定义类型&#xff0c;在我们C语言中&#xff1a;自定义类型并非只有结构体…