双指针算法——部分OJ题详解

目录

关于双指针算法:

1,对撞指针

2,快慢指针

部分OJ题详解 

283.移动零

1089.复写零

202.快乐数

11.盛水最多的容器

611.有效三角形的个数

剑指offer 57.和为s的两个数字

15.三数之和

18.四数之和


关于双指针算法:

双指针算法是一种经典算法,一般有两种:对撞指针和快慢指针 。

1,对撞指针

一般用于顺序结构中,也叫做左右指针。意思是从定长顺序结构的最左端和最右端向中间移动,当在循环内部找到结果或者满足:left == rightleft > right时就退出循环

2,快慢指针

和如名字一样,两个指针在顺序结构的同一端同时移动,一个指针一次往后移动一次,一个一次移动两次。该算法非常适合用于处理数组和环形数据结构问题。

部分OJ题详解 

283.移动零

283. 移动零 - 力扣(LeetCode)

 该题目可归类为“数组划分,数组分块”问题,可以使用快慢指针将整个数组分成0区间和非0区间。

定义两个指针:cur和dest。其中cur的作用很简单,就是从左往右遍历数组,dest的作用则是划分0区间和非0区间,表示已经划分好的0区间的最后一个如下图:

class Solution {
public:void moveZeroes(vector<int>& nums) {for(size_t cur = 0, dest = -1; cur < nums.size(); cur++){if(nums[cur] != 0) {swap(nums[++dest], nums[cur]);}}}
};

1089.复写零

1089. 复写零 - 力扣(LeetCode)

该题目简单解释就是:给一个数组arr,将每一个出现的0往后复写一位,不覆盖后面的数据,其余元素往后移动,不要越界,只能修改原数组。

咱们来分析这道题:

  1. 首先我们要找到最后一个要复写的数的位置下标,定义双指针cur=0,dest=-1,cur先开始遍历,如果cur不为0,dest和cur都走一步,如果cur为0,dest走两步,最后当dest走到数组结尾时,开始复写0
  2. 有个特例,[1, 0, 2, 3, 0 ,4],该数组按照上面的步骤,最后的cur会指向第二个0,dest最后走到了第7个位置也就是数组后面一个位置,但是由于cur为0,所以在复写的时候会把dest改为0,造成越界访问,所以需要处理下越界情况
  3. 当最后dest == arr.size()时,arr[n - 1] = 0; dest += 2;
class Solution {
public:void duplicateZeros(vector<int>& arr){//1,先找到最后一个数int cur = 0, dest = -1;int n = arr.size();while(cur < n){if(arr[cur] == 0){dest += 2;}else{dest ++;}if(dest >= n - 1) break;//当dest合法时,在cur++cur++;}//2,处理边界情况if(dest == n){arr[n - 1] = 0;cur--;dest += 2;}//3,开始从后往前复写0while(cur >= 0){if(arr[cur] != 0){arr[dest--] = arr[cur--]; }else{arr[dest--] = 0;arr[dest--] = 0; //复写0cur--;}}}
};

202.快乐数

202. 快乐数 - 力扣(LeetCode)

 题目有点复杂,但是相信大家看来示例之后很快能理解,下面我们来分析下这个题目:

  1. 这道题一共有三种情况,第一种情况是:和示例一样,输入19,最后会变成1,变成1之后会陷入无限循环    第二种情况是:输入2,无限循环,但始终变不到1    第三种情况就是始终变不到1并且无重复数字,但该题不考虑
  2. 以第一第二种情况为例,最后都会变成一个数并且无限循环,也就是说两种情况带入链表的话最后会有一个“环”,所以这道题和我们的“判断链表是否有环”很相似,这道题我们只需要判断环种的数是否为1即可
  3. 所以我们采用快慢指针来解,然后因为的特性,只要判断两个指针相遇时的值即可
  4. 最后,我们可以直接用数来代替指针的租用,假设slow指针=2,那么平方之后变成了4,就相当于在数组中对指针进行了++,这是慢指针,所以快指针进行两次平方,也就是++两次
class Solution {
public:int bitSum(int n) //返回n数每一位上的平方和{int sum = 0;while(n > 0){int t = n % 10;sum += t*t;n /= 10;}return sum;}bool isHappy(int n) {int slow = n, fast = n;fast = bitSum(fast);while(fast != slow){slow = bitSum(slow);fast = bitSum(fast);fast = bitSum(fast);}return slow == 1;}
};

11.盛水最多的容器

11. 盛最多水的容器 - 力扣(LeetCode)

解释一下题目:给一个长度为n的整数数组height,每个元素代表一个垂线表示容器的高度,找出其中两条线,使得它们与x轴共同构成的容器可以容纳最多的水,返回容器可以存储的最大水量。下面我们来分析下这道题:

  1. 首先是暴力解法:用两个for循环将所有的可容纳的水的容量全部枚举出来,列成数组然后排序,选出最大值。这种解法肯定会超时,因为时间复杂度为O(N*2)
  2.  我们先选一个区间:[6, 2, 5, 4],首先计算6到4的容量,为6 * 3 = 18,当向内枚举时,也就是2和4,5和4,容器的宽度是一直在减小的,但是高度会有两种情况:当向内枚举是碰见了一个小的数(2比4小),所以高度减小;当碰到了一个大的数(5比4大),虽然高度不变,但是宽度减小了,所以总体容量还是在减小。那么我们就可以去掉2和4,5和4的枚举,就能减少枚举次数
  3. 然后就是总体了[1, 8, 6, 2, 5, 4, 8, 3, 7]。先算1和7,得出v1,1比7小,所以左指针++;计算8到7,得出v2,8比7大,所以右指针--;再算8到3,得出v3,8比3大,所以右指针++
  4. 像这样利用单调性和双指针来搞,只需要遍历一遍数组就可以了,就可以把暴力解法的O(N * 2)变成了O(N)
class Solution {
public:int maxArea(vector<int>& height) {int left = 0, right = height.size()-1, ret = 0;while(left < right) //阻止条件就是两个指针都往中间移动,直到相遇就停下来{int v = min(height[left], height[right]) * (right - left);ret = max(ret, v);//计算完毕,移动指针,谁小移动谁if(height[left] < height[right]){left++;}else{right--;}}return ret;}
};

611.有效三角形的个数

611. 有效三角形的个数 - 力扣(LeetCode)

解释下这道题:给我们一个数组,选择数组中任意三个数尝试组成一个三角形,返回能够组成三角形的个数,下面我们来分析下这道题:

  1. 首先是暴力解法,既然要找三个数,那就用三个循环穷举所有情况,然后只需判断a+b是否大于c即可,但是我们可以先对数组排序,这样原本要三个数各判断一次,排序后只需判断一次
  2. 排完序后再进行列举,只有两种情况:①a+b>c    ②a+b <= c,我们只需对这两个情况做判断即可
  3. 假设要处理的数组为[2, 2, 3, 4, 5, 9, 10],我们可以先固定最大值10,然后定义双指针,left指向2,right指向9;2+9>10 符合情况①,这时候可以看到2后面的值都比2大,那么2后面的数和9相加肯定是大于10的,所以就不要再left++了,直接将后面的情况全部纳入,也就是可形成的三角形数直接+(right-left),就可以省去很多次计算和比较
  4. 那么left不要动,那么自然是right往左移了,right接着指向5,这时候2+5<10,符合情况②,那么right就不要再往左走了,因为5左边的数肯定比5小,不可能组成三角形,所以要left++再判断
  5. 然后按照上面的步骤把固定10的情况全搞定,然后再固定9,这样依次判断,就能用一个循环解决问题
class Solution {
public:int triangleNumber(vector<int>& nums) {//1,排序优化sort(nums.begin(), nums.end());//2,采用双指针解决问题int ret = 0;//统计最终个数int n = nums.size();for(int i = n-1; i >= 2; i--){//利用双指针快速统计符合要求的三元组个数int left = 0, right = i - 1;while(left < right){//判断能否构成三角形if(nums[left] + nums[right] > nums[i]) //符合情况①{ret += right - left;right--;}else //符合情况②{left++;}}}return ret;}
};

剑指offer 57.和为s的两个数字

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

解释下这道题:给一个升序的数组和一个数字s,在数组里找两个数使和等于s,如果有多种结果返回一组即可,下面我们来分析下这道题:

  1. 首先还是暴力解法:两个for循环穷举全部情况,一个一个相加判断即可,但是暴力解法没有用到数组已经有序这个条件,所以这道题我们还是用单调性和双指针来解决。
  2. 假设给的数组是[2, 7, 11, 15, 19, 21],s=30;所以当相加的时候会有三种情况①sum>t    ②sum<t    ③sum=t。
  3. 首先定义left指针指向2,right指针指向21,2+21<30,符合情况②,所以21前面的数都小于21,所以直接全部排除,right不动,left++到7的时候仍然小于20,left再++
  4. 当left指向11时,11+19==30,符合要求于是返回,如果再次符合情况②,left再++,如果符合情况①,那么right--
  5. 就这样我们只需要遍历一次数组就能找到等于30的两个数了
class Solution 
{
public:vector<int> twoSum(vector<int>& nums, int s) {int left = 0, right = nums.size() - 1;while(left < right){if(nums[left] + nums[right] > s){right--;}else if(nums[left] + nums[right] < s){left++;}else{return {nums[left], nums[right]};}}return {-1};}
};

15.三数之和

15. 三数之和 - 力扣(LeetCode)

 解释一下题目:给你一个数组,在这个数组中随便选三个数使它们的和为0,并且三个数互不相同,返回所有相加为0的三个数组合,顺序没有要求但是元素的下标不能一样,假设输入:[-1, 0, 1, 2, -1, -4]  ,我们可以选出三个[-1, 0, 1] [-1, 2, -1] [0, 1, -1]但是只返回前两个,是因为第三个和第一个顺序不一样,但是元素一样的,这样的情况我们二选一返回。下面我们来分析下这道题:

  1. 首先是暴力解法,三个for暴力穷举,然后将每个选出来的三个数组成vector,然后排序+去重,去重可以用set容器去重,但是暴力解法时间复杂度为O(N^3)
  2. 其实这道题和上面的“和为s的两个数”的题目很像,只是s变成了0,两数之和变为了三数之和,所以我们也可以尝试用排序+双指针来解决
  3. 首先对原数组进行排序,假设排完序后是[-4, -4, -1, 0, 0, 0, 1, 1, 4, 4, 5, 6],可以先固定第一个-4,i=-4,然后定义双指针left指向第二个-4,right指向6,然后可以和“有效三角形个数”题目一样,找到left和right的和等于 -(-4)即可,然后再次固定-1,重复操作即可
  4. 但是这道题有一些细节需要额外处理:①不借助set去重,原数组排序后已经有序,所以找到一种结果之后,left和right都要跳过重复元素,并且使用完一个固定数i之后,i也要跳过重复元素,并且,跳过重复元素的时候也要注意越界问题,比如[0, 0, 0, 0]
  5. ②找到一种结果之后,指针不要停,要继续往里缩继续找
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums){vector<vector<int>> vv;//1,排序sort(nums.begin(), nums.end());//2,利用双指针解决问题int n = nums.size();for(int i = 0; i < n;) //固定数a{if(nums[i] > 0) break; //优化:如果最小的数已经大于0,那么后面的数相加一定不等于0int left = i + 1, right = n - 1, a = -nums[i];while(left < right){int sum = nums[left] + nums[right];if(sum > a)right--;else if(sum < a)left++;else{vv.push_back({nums[i], nums[left], nums[right]});left++;right--; //不漏while(left < right && nums[left] == nums[left - 1]) left++; //跳过重复元素while(left < right && nums[right] == nums[right + 1] ) right--; //对指针去重}}//对i去重i++;while(i < n && nums[i] == nums[i - 1]) i++;}return vv;}
};

18.四数之和

18. 四数之和 - 力扣(LeetCode)

解释下题目:和三数之和题目一样,只是变成了4个数的和为0,下面来分析下这道题:

  1. 暴力解法就是还是4次循环 + set去重,但是时间复杂度为O(N^4),巨耗时间
  2. 其实我们可以复用我们三数之和的算法,固定一个数,然后在这个数后面使用三数之和算法,然后再把固定的数往后面挪,再使用三数之和,依次循环就可以解决四数之和的问题
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> vv;//1,排序sort(nums.begin(), nums.end());//2,利用双指针解决问题int n = nums.size();for(int a = 0; a < n;) //固定数a{//利用三数之和解决问题for(int b = a + 1; b < n;){//双指针int left = b + 1, right = n -1;long long aim = (long long)target - nums[a] - nums[b]; //利用双指针找到两个数的和等于aim即可while(left < right){int sum = nums[left] + nums[right];if(sum < aim) left++;else if(sum > aim)right--;else{vv.push_back({nums[a], nums[b], nums[left++], nums[right--]}); //找到一种结果//去重操作 --> ①先去重left和right,②再去重b,③最后再a去重//去重①while(left < right && nums[left] == nums[left-1]) left++;while(left < right && nums[right] == nums[right+1]) right--;}}//去重②b++;while(b < n && nums[b] == nums[b-1]) b++;}a++;while(a < n && nums[a] == nums[a-1]) a++;}return vv;}
};

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

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

相关文章

[240622] X-CMD 发布 v0.3.12: 引入 codeberg,增强传统命令,改善对 Elvish 和 Fish 支持

目录 X-CMD 发布 v0.3.12✨ cb&#xff08;codeberg.org) &#xff0c;fjo&#xff0c;gitea✨ Elvish✨ fish✨ git✨ ls✨ last✨ ps✨ stat✨ id X-CMD 发布 v0.3.12 ✨ cb&#xff08;codeberg.org) &#xff0c;fjo&#xff0c;gitea 本次版本实验性引入了这三个代码仓库…

个性化光标和动态壁纸

光标 进入这个宝藏网页至美化 至美化 进入鼠标页面&#xff0c;选择自己喜欢的鼠标&#xff0c;进入相关页面 分为两种&#xff0c;那么热爱有钱的UU可以选择高清版 像我这种没钱的孩子或者觉得试用版够用的就使用上面的 点击下载 进入自己的文件夹&#xff0c;解压成功之…

【记录】使用远程SSH配置d2l环境(含装pytorch,同时适用于本地anaconda)

文章目录 前言一、从创建新环境开始二、使用步骤1.安装pytorch2.安装 d2l 包3.安装其他包4.使用jupyter notebook 前言 记录一下如何利用使用命令行进行anaconda配置 d2l环境、pytorch并进行训练深度学习模型。 一、从创建新环境开始 如果是本地直接装一个 anaconda 软件就行…

ReactNative和Android通信

初始化一个RN项目以后&#xff0c;接下来想要让Android与React Native通信 写一个继承自ReactContextBaseJavaModule类的子类&#xff0c;重写getName方法 package com.awesomeprojectimport android.util.Log import android.widget.Toast import com.facebook.react.bridge.…

MFC学习--CListCtrl复选框以及选择

如何展示复选框 //LVS_EX_CHECKBOXES每一行的最前面带个复选框//LVS_EX_FULLROWSELECT整行选中//LVS_EX_GRIDLINES网格线//LVS_EX_HEADERDRAGDROP列表头可以拖动m_listctl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES); 全选&#xff0c;全…

解析 flink sql 转化成flink job

文章目录 背景流程flink实例实现细节定义的规则定义的物理算子定义的flink exec node 背景 在很多计算引擎里&#xff0c;都会把sql 这种标准语言&#xff0c;转成计算引擎下底层实际的算子&#xff0c;因此理解此转换的流程对于理解整个过程非常重要 流程 flink实例 public…

视听分割相关论文阅读

1. End-to-End Referring Video Object Segmentation with Multimodal Transformers RVOS&#xff08;视频中的参考对象分割&#xff09;比RIS&#xff08;图像中的参考对象分割&#xff09;要困难得多&#xff0c;因为指代动作的文本表达通常无法从单个静态帧中正确推断出来。…

使用J-Link Commander查找STM32死机问题

接口:PA13,PA14&#xff0c;请勿连接复位引脚。 输入usb命令这里我已经连接过了STM32F407VET6了。 再输入connect命令这里我已经默认选择了SWD接口&#xff0c;4000K速率。 可以输入speed 4000命令选择4000K速率: 写一段崩溃代码进行测试: void CashCode(void){*((volatil…

区块链技术原理

1.起源&#xff1a; ➢ 中本聪(Satoshi Nakamoto), 2008 ➢ 比特币:一种点对点的电子现金系统 2.分布式账本技术原理 ➢ 将交易向全网所有节点进行广播 ➢ 众多记账节点对记账权达成共识&#xff0c;由共识确认的记账节点把记账区块发布给全网 ➢ 所有账本数据完整存储于区块…

机器学习基础:与Python关系和未来发展

目录 初识Python Python的由来 自由软件运动 编译方式的演进 Python语言的特点 语法简单&#xff0c;易于理解 语法结构清晰&#xff0c;快速上手 丰富的第三方库 机器学习 监督学习 无监督学习 半监督学习 欢迎回到我们的神经网络与深度学习Tensorflow实战学习&am…

MVVM架构详解:前端开发的理想选择

目录 前言1. MVVM架构概述1.1 MVVM架构的定义1.2 MVVM与MVC的区别 2. MVVM架构的核心组件2.1 模型&#xff08;Model&#xff09;2.2 视图&#xff08;View&#xff09;2.3 视图模型&#xff08;ViewModel&#xff09; 3. MVVM架构的优势3.1 分离关注点3.2 提高代码可测试性3.3…

AI大模型企业应用实战(14)-langchain的Embedding

1 安装依赖 ! pip install --upgrade langchain ! pip install --upgrade openai0.27.8 ! pip install -U langchain-openai ! pip show openai ! pip show langchain ! pip show langchain-openai 2 Embed_documents # 1. 导入所需的库 from langchain_openai import Open…

嵌入式中逻辑分析仪与示波器的基本原理

大家好,今天主要给大家分享一下,嵌入式中如何使用逻辑分析仪和示波器的方法,希望对大家有所帮助。 https://dreamsourcelab.cn/ 第一:什么是逻辑分析仪 是否遇到使用示波器分析数字电路的冏境:深度不够,时间太短,无法抓到想要的波形,没有协议内容解析? 逻辑分析仪…

Pnpm:包管理的新星,如何颠覆 Npm 和 Yarn

在探索现代 JavaScript 生态系统时&#xff0c;我们常常会遇到新兴技术的快速迭代和改进。其中&#xff0c;包管理工具的发展尤为重要&#xff0c;因为它们直接影响开发效率和项目性能。最近&#xff0c;pnpm 作为一种新的包管理工具引起了广泛关注。它不仅挑战了传统工具如 np…

vue3路由的使用

1、引用路由组件 npm install vue-router 2、创建路由 根据项目结构创建对应的组件&#xff08;home\news\about&#xff09; 在 src 目录下创建 router/index.ts //引入路由 import { createRouter,createWebHistory,createWebHashHistory } from vue-router import Home …

关于Pytorch转换为MindSpore的一点建议

一、事先准备 必须要对Mindspore有一些了解&#xff0c;因为这个框架确实有些和其它流程不一样的地方&#xff0c;比如算子计算、训练过程中的自动微分&#xff0c;所以这两个课程要好好过一遍&#xff0c;官网介绍文档最好也要过一遍 1、零基础Mindspore&#xff1a;https://…

linux服务器运行pycharm代码

一、pycharm代码上传服务器 1、进行配置 2、建立ssh连接&#xff08;选择文件传输协议SFTP&#xff09; 3、设置服务器名&#xff08;自定义&#xff09; 4、点击SSH配置右侧的"…"&#xff0c;进行SSH内容设置&#xff1a; 5、输入服务器信息 6、进行本地项目与远程…

AIGC系列之一-一文理解什么是Embedding嵌入技术

摘要&#xff1a;嵌入技术&#xff08;Embedding&#xff09;是一种将高维数据映射到低维空间的技术&#xff0c;在人工智能与图形学研究中被广泛应用。本文将介绍嵌入技术的基本概念、原理以及在 AIGC&#xff08;Artificial Intelligence and Graphics Computing&#xff09;…

基于STM32的智能农业灌溉系统

目录 引言环境准备智能农业灌溉系统基础代码实现&#xff1a;实现智能农业灌溉系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统实现4.4 用户界面与数据可视化应用场景&#xff1a;智能农业管理与优化问题解决方案与优化收尾与总结 1. 引言 智能农业灌溉系统通过使用ST…