“双指针”算法下篇

WeChat_20240806081335


 对双指针这一思想在OJ 里面的相关应用,感兴趣的友友们,可以看下此篇博客

https://blog.csdn.net/X_do_myself/article/details/141291451?spm=1001.2014.3001.5502

目录
一·盛最多水的容器
1·题目链接:盛最多水的容器

2· 分析

1)解法一 : 暴力求解

就是逐一进行判断,找出乘积相乘最大的:比如说第一个元素与第二个元素进行结合得出 1* 1 = 1

第一个元素与第三个元素进行结合得出 1*2 = 2……经过一轮的循环,最终得出这一轮乘积最大的

是1*8 = 8

接下来进行第二轮的循环:第二个元素与第三个元素结合,与第四个元素结合……

最终经过几轮循环得出最大乘积:49,即为所求。

对应时间复杂度O(N^2)

 但是这并不是一个优解的算法

2)解法二 :双指针

这个双指针代表的意义:表示一个 范围:所有边界位置的一个范围

3· 算法原理

1)先假设当前左右指针所在位置对应的得出的体积是 V 

2)比较此时 left 和 right 所对应的元素大小,让指向元素小的指针进行移动

3)此时left ,right 指向的位置可以再次得出一个体积,与之前所得出的 V 进行比较,取最大者即

可,重复上述过程,直至left ,right  指针相遇

4)注意细节处理:计算体积之前,需要先求出 2指针所指向元素最小的是哪一个

4· OJ代码
class Solution {
public:int maxArea(vector<int>& height) {int n = height.size();int left = 0,right = n-1;int v = 0;while(left < right){int ret = min(height[left],height[right]) * (right-left);v = max(v,ret);//代码核心// 找高度最小的if(height[left] > height[right] )right--;else++left;}return v;}
};
二·找出和为 s 的两个数
1·题目链接 两数之和
2· 分析

解法一:暴力枚举

对于这个思想,想必各位都不陌生吧,直接2个  for 循环,判断nums[left]  + num[right] 是否为

target 即可

时间复杂度:O(N^2)

解法二:双指针

针对暴力枚举,我们还可以进行优化:

定义2个指针 :left ,right 

3· 算法原理

1) 排序(注意:不要直接对原始数组进行变动)

2)指针移动

初始化:left = 0,right = n-1(n: 数组大小)

num[left] + num[right] > target ,right --: 因为此时是一个有序的数组,num[left]  <=  num[right] 一定

是成立的,当num[left] + num[right] > target  的时候就需要缩小数据范围,进行左移

num[left] + num[right]  <  target ,left++:   同理,一个 有序的数组,num[left] + num[right]  < target ,

此时就需要增大数据,右移

num[left] + num[right] = target :记录此时的元素数值

3)把当前 num[left]  num[right] 进行保存一下

4) 遍历原始数组,找到指定元素之后,返回对应的元素下标

注意此时不能一次性就把 num[left] num[right]遍历完

4· OJ代码
class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> ret;vector<int> tmp(nums.begin(),nums.end());int f1 = 0,f2 = 0;//1.排序sort(tmp.begin(),tmp.end());//2. 双指针int left = 0;int right = nums.size()-1;while(left < right){if(tmp[left]+ tmp[right] > target)--right;else if(tmp[left]+ tmp[right] == target){f1 = tmp[left];f2 = tmp[right];break;}else++left;}for(int i = 0;i<nums.size();i++){if(nums[i] == f1){ret.push_back(i);break;}}for(int i = 0;i<nums.size();i++){if(i != ret[0] && nums[i] == f2){ret.push_back(i);break;}}return ret;}
};
三·有效的三角形个数
1·题目链接  有效的三角形个数
2· 分析

构成三角形的条件:

任意的两边之和都大于第三边  或者是  任意的两边之差都小于第三边

解法一:暴力枚举

三层循环,假设第一次对应数为 a , b ,c

只需要判断 a+b > c,a+c > b, b+c > c 这三个条件是否同时满足即可。

对应时间复杂度 :O(N^3)

解法二:双指针

假设此时固定一个最大的数,只需要在最大的数左区间里面找出 两个数 a+b > 最大的数即可

不知道看到这,各位是否已经有了思路?

 此时问题就已经简化成了:找两数之和大于 S 的问题

这不就是上面找出两数之和为S 的一个变形嘛。

3· 算法原理

我们知道   a<=   b   <=  c  ,一定满足  a+ b > c  这个条件,此时,a,b ,c 这个组合移动可以构成有

效的三角形。

 1)先进行排序

2)从右向左依次固定一个最大的数

3) 指针移动

当 num[left] + num[right ] > c , 此时就是对应的 right - left  j就是这一轮匹配到的个数,同时right 

指针向左移动

因为此时数组是有序的,num[right ] 必定是大于或者等于 num[left ] ,right  向左移动是缩小 

num[left] + num[right ] 的范围

num[left] +num[right ] <= c,left 指针向右移动 : 此时 num[left] 小于或者等于 num[right] ,需要找

下一个匹配的组合,只能left ++ 

草图分析:

 

 

4· OJ代码
class Solution {
public:int triangleNumber(vector<int>& nums) {//排序sort(nums.begin(),nums.end());int ret = 0;int n  = nums.size();//依次固定最大的数for(int max_i = n-1;max_i >= 2 ;--max_i){int left = 0,right = max_i-1;//注意更新依次max_i,左右指针都要进行改变,所有不能定义在外面while(left < right){if(nums[left] +nums[right]> nums[max_i]){ret += right-left;right--;}else // <= max_i ,对当前指向的数据扩大++left;}}return ret;}
};

 四· 三数之和

1. 题目链接  三数之和
2. 分析
解法一:暴力枚举

3个 for 循环,依次判断 所得之是否为0 即可

对应的伪代码

    for (int i = 0; i < n - 2; ++i){for (int j = i + 1; j < n - 1; ++j){for (int k = j + 1; k < n; ++k){if(num[i]+num[j]+num[k] == 0)}}}

 

细节处理:题目要求三元组不能重复 

时间复杂度:O (N^3) 

前面我们做过两数之和,其实此题就是上面的变形

解法二:双指针结合单调性

3. 算法原理

为了下面描述方便:假设这三个数分别对应 a, b ,c

1) 排序

2)先固定一个数 a , 接下来只需要在 数a 的右边区间找到 b + c = target - a 的两个数

3)指针移动

当 num[left] + num[right] > target -a ,right -- :依然是借助单调性

注意right  再向左移动的过程中,可能会遇到重复的数据 ,需要进行去重的处理

当 num[left] + num[right]  < target-a ,left++:

注意left  再向y右移动的过程中,可能会遇到重复的数据 ,需要进行去重的处理

当 num[left] + num[right]  ==  target-a ,此时把对应的 数据放到一个变量里面,继续进行查找

4) 当left right,相遇的时候,就对a  进行更新,依然注意去重的处理 

4. OJ代码
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> ret;int  n = nums.size();//1. 排序sort(nums.begin(),nums.end());// 2. 双指针结合单调性for(int i = 0; i < n-2 && nums[i] <= 0;){int left = i+1,right = n-1;while(left < right){if(nums[left] + nums[right] == -nums[i]){ret.push_back({nums[i],nums[left],nums[right]});--right;//去重while(left < right && nums[right] == nums[right+1] ){--right;}}else if (nums[left] + nums[right] < -nums[i]){++left;//去重while(left < right && nums[left] == nums[left-1]){++left;}}else  // (nums[left] + nums[right] > -nums[i]){--right;//去重while(left < right && nums[right] == nums[right+1] ){--right;}   }}++i;//去重while(i < n-2 && nums[i] == nums[i-1]){++i;}}return ret;}
};

 五· 四数之和

1. 题目链接  四数之和

2. 分析

想必各位看到这里,应该自认为小case ,这不就是轻松拿捏嘛。双指针走起

解法一:暴力枚举

解法二:双指针结合单调性

这里分析的思路和上面的找三数之和是一样滴

3. 算法原理

1)排序

2)依次固定 2个数,借助循环

3)指针移动

4)去重问题

这里不仅仅涉及到left ,right 去重,还有在固定2个数的时候也需要去重

5)注意数据的溢出

在我们提交代码的时候发现一下报错:

此报错的意思:有符号数据的溢出问题 

解决:

此时只需把int 改为long 即可 

4. OJ代码
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;//排序sort(nums.begin(),nums.end());int n = nums.size();//先依次枚举第一个数for(int n1 = 0;n1 < n-3; ){//依次枚举第二个数for(int n2 = n1+1;n2 < n-2 ; ){//双指针+单调性  //注意溢出问题//超出int范围long add =(long) target-(long)nums[n1]-(long)nums[n2];// int add = target-nums[n1]-nums[n2];int left = n2+1,right = n-1;while(left < right){if(nums[left] + nums[right] ==add ){ret.push_back({nums[n1],nums[n2],nums[left],nums[right]});--right;//去重while(left < right && nums[right] == nums[right+1]){--right;}}else if(nums[left] + nums[right]  > add ){--right;//去重while(left < right && nums[right] == nums[right+1]){--right;}}else  // (nums[left] + nums[right] < add ){++left;while(left < right && nums[left] == nums[left-1]){++left;}}}++n2;//去重while(n2 < n-2  && nums[n2] == nums[n2-1]){++n2;}}//去重++ n1;while(n1 < n-3 && nums[n1] == nums[n1-1]){++n1;}}return ret;}
};

结语:

对于双指针的思想,我们可以通过以上OJ 题总结一下。当题目要求我们进行一些操作之后,会对

数组分成3部分区间:[0,left-1] [left right ] [right+1, n-1]  ,多数都是“原地”对数组进行操作,此时

可以借助双指针的思想,注意指针的移动,下标的确定

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

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

相关文章

音频分割软件有什么?最方便的音频分割软件分享给你

一段长音频就像是一本厚重的百科全书&#xff0c;而音频剪辑师的任务&#xff0c;就是要将这本书拆分成数个章节&#xff0c;每章都有其独立的主题和内容&#xff0c;这非常考验剪辑师们的音频分割技巧。 幸运的是&#xff0c;随着技术的发展&#xff0c;市面上出现了许多优秀…

在 Spring Boot 中为 MyBatis 添加拦截器

在 Spring Boot 中为 MyBatis 添加拦截器以实现分表查询涉及几个步骤。以下是如何在 Spring Boot 应用中配置和使用 MyBatis 拦截器的指南&#xff0c;具体以分表查询为例&#xff1a; 创建拦截器 首先&#xff0c;定义一个自定义的 MyBatis 拦截器&#xff0c;实现 Intercept…

【八股】DDD领域驱动设计

什么是 DDD 领域驱动设计 🔜 是一种软件设计方法,核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型和代码模型的一致性。它倡导统一语言,提出了一系列概念,包括实体、值对象、聚合根等。 🔜 优势:帮助开发团队更好地理解和表达业务…

微信小程序引入全局环境变量

有时候一套代码要在多个小程序appId下使用,其中又有一些数据(文字)需要做区分.可以使用下面的方法 把要配置的数据以export default 形式导出 在app.js中,引入project.config.0.js文件,将导出的数据放在globalData中 在页面目录中,即可利用getApp()方法使用全局变量 也可以放数…

剪辑视频的软件在手,温馨瞬间秒变电影级大片

在现在这个啥都能数字化的年代&#xff0c;家里拍的视频成了咱们记录生活、留下美好回忆的好办法。不过&#xff0c;好多人可能就只是随便拍拍&#xff0c;然后直接发出去&#xff0c;没想过用专业的剪辑视频的软件来搞一搞&#xff0c;让自己的视频更有感觉&#xff0c;看起来…

Python-基础-面向对象

文章目录 面向对象1 简介2 基本操作2.1 类2.2 对象2.3 继承 面向对象 1 简介 面向对象&#xff08;OOP&#xff09;是一种对现实世界理解和抽象的方法&#xff0c;对象的含义是指在现实生活中能够看得见摸得着的具体事物&#xff0c;一句比较经典的描述是一切皆对象&#xff…

微服务通信

一、Feign远程调用 Feign是Spring Cloud提供的⼀个声明式的伪Http客户端&#xff0c; 它使得调⽤远程服务就像调⽤本地服务⼀样简单&#xff0c; 只需要创建⼀个接⼝并添加⼀个注解即可。 Nacos很好的兼容了Feign&#xff0c; Feign 默认集了Ribbon&#xff0c; 所以在Nacos下…

vue全局参数

/* eslint-disable no-new */ new Vue({el: #app,router,components: { App },template: <App/>,data:function(){return{wbWinList: [] // 定义的变量&#xff0c;全局参数}}, }) //使用全局参数 // this.$root.backgroundColor 666;其它页面如果想监听改变 //监听全…

git cherry-pick 用法

/* * cherry-pick */ git cherry-pick 允许开发者有选择地合并其他分支的提交到当前分支&#xff0c; 这对于解决代码冲突、修复bug以及在不同分支之间转移提交非常有用。 基本用法&#xff1a; 1.合并一个提交&#xff1a;通过指定提交的哈希值&#xff0c…

polarctf靶场[CRYPTO]显而易见的密码、[CRYPTO]夏多的梦、[CRYPTO]再这么说话我揍你了、[CRYPTO]神秘组织M

[CRYPTO]显而易见的密码 考点&#xff1a;ntlm编码 打开文件&#xff0c;显示内容就是ntlm格式 ntlm解密 在线网站&#xff1a; https://www.cmd5.com/便可得到flag [CRYPTO]夏多的梦 根据题目提示可以猜测为夏多密码 考点&#xff1a;夏多密码 在线加密原理网站&#x…

使用axios的fetch adapter遇到浏览器兼容问题

axios 2024年5月20号发布1.7.0版&#xff0c;开始支持fetch adapter&#xff0c;我是很高兴的&#xff0c;把此前fetch写的语句换成axios的&#xff0c;感觉简洁了很多。 因为我要post传urlencode编码参数&#xff0c;所以用fetch得这样&#xff1a; let data{k1:v1,k2:v2,k3:…

链表OJ题——链表的回文结构

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 链表的回文结构 二、解题思路 三、解题代码

探索JetBrains IDE数据库驱动的存储位置及离线安装指南

JetBrains IDE&#xff0c;如IntelliJ IDEA和GoLand&#xff0c;是广受开发者喜爱的集成开发环境。它们提供了强大的数据库支持功能&#xff0c;包括数据库连接和操作。了解数据库驱动的存储位置对于进行离线安装和配置非常重要。 理解数据库驱动 数据库驱动是软件应用程序与…

4种方法!前端判断页面是在PC端还是移动端打开

4种方法&#xff01;前端判断页面是在PC端还是移动端打开 在写前端的过程中&#xff0c;有时候考虑适配与页面权限等问题的时候&#xff0c;需要涉及到用户访问的设备情况&#xff0c;下面将介绍几种常见的判断页面是在PC端还是移动端打开的方法。 方法1. 基于 navigator.use…

【JAVA基础】类与对象

文章目录 Java类Java对象为什么Java会设计对象Java对象怎么用程序的执行流程 Java类 JAVA类&#xff1a;项目中新建的.java文件就是JAVA类&#xff0c;其中包含代码&#xff0c;通过javac命令编译为二进制.class文件&#xff0c;让计算机读取。类是对象的模板 Java对象 JAVA…

【K8s】专题十二(2):Kubernetes 存储之 PersistentVolume

本文内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01; 如果对您有帮助&#xff0c;烦请点赞、关注、转发、订阅专栏&#xff01; 专栏订阅入口 Linux 专栏 | Docker 专栏 | Kubernetes 专栏 往期精彩文章 【Docker】&#xff08;全网首发&#xff09;Kyl…

Python学习-打工人挣钱

编程案例 example one: # 假设小王每天可以挣500元,周内每天花60,周末每天花100,一年365天可以挣多少钱?MoneySum=0 for i in range(365):if i % 7 in [6,0]:MoneySum

笔试题(6)

1 、static 的用法&#xff08;定义和用途&#xff09; 1&#xff09;用 static 修饰局部变量&#xff1a;使其变为静态存 储方式(静态数据区)&#xff0c;那么这个局部变量在函 数执行完成之 后不会被释放&#xff0c;而是继续保留在内存中。 2&#xff09;用 static 修饰全局…

【C++题解】1146. 求S的值

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1146. 求S的值 类型&#xff1a;递归基础、函数 题目描述&#xff1a; 求 S12471116…的值刚好大于等于 5000 时 S 的值。 输入&#xff1a; 无。 输出&#xff1a; 一行&…

自然语言处理系列三十四》 语义相似度》同义词词林》代码实战

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列三十四HanLP自然语言处理工具包代码实战同义词…