基础算法--双指针算法

文章目录

  • 什么是双指针算法
  • 例题
    • 1.移动零
    • 2.复写零
    • 3.快乐数
    • 4.盛最多水的容器
    • 5.有效三角形的个数
    • 6.三数之和
    • 7.四数之和

在这里插入图片描述

什么是双指针算法

通常我们讲的双指针就是用两个指针,两个指针可以是快慢指针,解决成环的问题,也可以是指向收尾的两个指针,来减小时间复杂度。双指针算法里的指针也不止是指针,在数组中也可以是数组元素的下标,这里双指针是一种思想,并不是单单指的是指针。
接下来我们用几道例题来看看双指针算法。

例题

1.移动零

题目:移动零

在这里插入图片描述

样例输出输入:

在这里插入图片描述

这道题要我们将所有零移到数组的末尾并且不改变数组的的非零元素的相对位置,并且有一个前提就是不能开辟新的数组,对原数组进行操作。
解法一:暴力解法
思路:两层循环,一层循环找零,一层循环移动零到最后一个零的位置。
解法二:双指针
思路:首先对于上面这个题可以这样理解,可以把他划分为两个大区间,一个已经排除过零的区间和一个未排除过零的区间,已排除过零的区间又可以划分为非零区间和一个只含零的区间所以这三个区间。
在这里插入图片描述
所以我们可以用一个指针指向非零与零的区间的分界线,再用一个指针对数组进行遍历,如果遇到零就继续++,因为零本来就该在最后,如果遇到非零数就交换非零区间和零区间的分解线的数,这里分界线取的是第一个零的位置。这样,当我们遍历到最后一个数之后,未划分区间就没有了,只剩下非零区间和零区间了。
代码展示:

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

2.复写零

题目:复写零

在这里插入图片描述

样例输入输出:

在这里插入图片描述

题目的意思就是让我们把数组当中的零在零之后复写但是复写之后数组的长度不变,所以后面数向后移动,所以最后就得到了样例输出。

思路:
首先我们先用异地操作模拟一下,开辟一个新的数组,然后进行拷贝,用两个指针,一个指针对原来的数组进行遍历,一个指针对新开辟的数组进行遍历,如果原来的数组遇到非零先判断新开辟数组是否越界,然后进行拷贝,两个指针同时向后移动,如果遇到零的话,还是先判断新开辟的数组是否越界,如果越界就停止,如果没越界拷贝在新开辟的数组中拷贝两个零,新开辟数组的指针向后移动两个单位,原来的数组向后移动一个单位。
上面讲的是异地操作,我们将异地操作优化为就地操作,首先我们考虑数组从左向右复写,首先这是不可能的,一个数组遍历一个数组进行复写的话会导致当我们复写的到零的时候后面的数已经被零覆盖了,所以遍历的时候后面会一直是零,复写就会一直复写零,所以这里我们考虑数组从后往前复写,如果从后往前复写的话我们就要考虑复写的最终位置,这里想到第一个办法就是双指针先就地模拟一遍异地操作,如果遇到零就+=2,如果遇到非零就+=1,这样用两个指针,最后一个指向末尾的元素就是就是需要拷贝的位置,另一个指针进行遍历数组并没有指向最后一个位置,所以这个位置指向的是复写的最后一个位置。通过模拟异地的复写我们已经找到最后一个复写的位置,现在只需要倒着复写即可,遇到零则复写两边,遇到非零则复写一遍。
代码展示:

class Solution
{
public:void duplicateZeros(vector<int>& arr) {//先找到最后一个复写位置,模拟异地操作int dest=-1;int cur=0;int n=arr.size();while(dest<n){if(arr[cur]!=0){dest++;}else{dest+=2;}if(dest>=n-1)break;cur++;}if(dest==n){arr[n-1]=0;dest-=2;cur-=1;}//从后向前完成复写操作while(cur>=0){if(arr[cur]!=0){arr[dest--]=arr[cur--];}else{arr[dest]=arr[cur];arr[dest-1]=arr[cur];dest-=2;cur-=1;}}}
};

3.快乐数

题目:快乐数

在这里插入图片描述

样例输入输出:

在这里插入图片描述

解法:
首先我们来看看什么事快乐数,如果各个位的平方相加,通过一系列重复操作之后,最后结果变为1,这就是快乐数,注意上面一句话很重要也就是无限循环,但可能不是1,这句话很重要。
首先我们用上面给出的两个例子来做样例:

在这里插入图片描述
上图上面样例的两个例子这样一看我们也就很熟悉了,这不是我们在链表做的链表带环的问题吗?判断链表是否带环只需要用一个快慢指针,如果两个指针,最后相遇则证明有环,如果两个指针没有相遇则证明最后没有环,这道题也很类似,这里的指针只需要抽象成操作的步骤,慢指针只操作一步,快指针操作两步,上面两种情况肯定是有环的,所以我们只需要判断两个指针相遇时的数是否是1,如果是1,则返回true,如果不是1,则返回false。

代码展示:

class Solution {
public://返回n这个数每一位上的平方和int bitsum(int n){int sum=0;while(n){int t=n%10;sum+=t*t;n/=10;}return sum;}bool isHappy(int n) {int slow=n;//slow等于第一个数int fast=bitsum(n);//fast等于第二个数while(fast!=slow){fast=bitsum(bitsum(fast));slow=bitsum(slow);}return slow==1;}
};

4.盛最多水的容器

题目:

在这里插入图片描述

样例输入输出:

在这里插入图片描述

题目意思很简单,这里的数组中的每个值代表的是高度,然后每个值对应的下标之差表示的是容器的宽度,宽度*高度就是我们水的最大容积。

解法一:暴力解法
暴力解法很简单,我们只需要用两个for循环每次for循环都记录一下这次的最大的容积,然后每次for循环完了之后,都对之前的容器的大小进行比较,如果新的容积大则更新容积,如果新的容积小,则不动。
解法二:双指针算法
首先我们先取首尾的指针,用下面的图讲解一下原理:
在这里插入图片描述
所以根据这个原理,向内取的话肯定是减小,所以这里我们每次肯定是小的高度进行–或者++。这里我们需要的变量就是两个首尾指针,然后还有一个记录最小值,最小值表示高度,因为高度是最小值决定的,因为向内取v是在不断减小的,所以这里我们每次更新的时候需要更新高度小的那个,更新高度大的那个会出现的情况可以看上面的图。
代码展示:

class Solution {
public://时间复杂度是O(N)int maxArea(vector<int>& height) {int tail=height.size()-1;int head=0;int v=0;while(head<tail){int Min=min(height[head],height[tail]);if(v<Min*(tail-head)){v=Min*(tail-head);}if(height[tail]<height[head]){tail--;}else{head++;}}return v;}
};

5.有效三角形的个数

题目:

在这里插入图片描述

输出输入样例:

在这里插入图片描述

解法一:暴力解法
这道题的暴力解法就是就是逐个遍历,将每一种情况遍历一遍,这道题由于不用去重,所以复杂程度直线下降,所以我们直接用三角形的判定的公式直接逐个情况判断就可以了,这里可以套三层循环,每种情况进行遍历,然后如果成立的话可以直接用一个count进行记录三角形成立的个数
解法二:双指针
步骤1:进行排序。
步骤2:排序之后,我们可以先固定最大的数,这里我们知道三角形满足a+b>c,这里c是最大的数,所以我们可以先固定最大的数,由于我们排序之后最大的数是最后一个数,所以这里我们可以直接取最后一个数为c,然后从c前面的数中取出a和b,但是a和b不是乱取的,我们先取最大的和最小的,也就是c前面的一个和第一个,如果最大的和最小的都能组成三角形,那么中间的组合肯定能组成三角形,因为中间的任何一个都比最小的大,所以这里可以直接计算三角形的个数的情况,三角形个数就是下标之差,接下来还有两种情况就是a+b==c或者a+b<c,这两种情况可以归为一种情况,就是left++,left向后移动,如果这种c的情况找完了,就可以将c–,继续找前面的情况满足的。
代码展示:

class Solution 
{
public:int triangleNumber(vector<int>& nums) {sort(nums.begin(),nums.end());int count=0;int left=0;int right=nums.size()-2;int c=nums.size()-1;while(c>=2){while(left<right){if(nums[left]+nums[right]>nums[c]){count+=(right-left);right--;}else{left++;}}//更新right和leftc--;left=0;right=c-1;}return count;}
};

6.三数之和

题目:

在这里插入图片描述

样例输出和输入:

在这里插入图片描述

这里我们可以直接看样例,题目的大概意思就是我们需要从数组中找三元组,并且三元组满足三元组的每个成员都是原数组中的不同位置的数,并且三元组不能有重复,三元组还要满足一个条件就是三元组之和加起来等于0。
解法一:暴力解法
这道题的难点就是在去重上,所以这道题我们可以用三个循环,然后把每个元素都遍历一遍用一个vector存储,最后把这个vector存在vectorvector中,如果是去重的话,我们可以直接用C++的一个容器就是unordered_set进行去重,最后直接把容器返回即可。

解法二:双指针
这里双指针和上一道题的双指针类似,还是需要固定一个数,这道题我们不用unordered_set进行去重,因为在算法题中可以用,但是在面试题中用unordered_set很可能会挂掉,所以我们海狮正常的用算法进行去重,首先我们海狮先排序,排序可以把相同的元素排在一起,排完序之后先固定一个数,我们先固定首元素,然后对首元素后面的数进行遍历,这里遍历只需要取首尾元素,这道题三数之和我们就转化为了两数之和,我们只需要求后面区间的数中存在两个数加起来等于前面这个数的相反数的组合即可,如果存在,则将其入到vector中,如果不存在则继续遍历,将固定的那个数往后移动,这里我们需要注意的细节是:我们后面的数加起来有三种情况:
第一种:大于前面的数,如果大于前面的数,可以直接将最大数–,因为这个数组被我们排成有序的了,所以不可能用left++,left++只会更大,所以应该是right–。
第二种情况:left+right=-a,这个情况直接入到数组中。
第三种情况:;left+right<-a,如果小于这个数,left++,如果right–只会更小。

接下来我们来谈谈去重的操作:
在这里插入图片描述
代码展示:

class Solution 
{
public:
vector<vector<int>> v;
vector<vector<int>> threeSum(vector<int>& nums)
{sort(nums.begin(), nums.end());int i = 0;while (i <= nums.size() - 2){int left = i + 1;int right = nums.size() - 1;while (left < right){vector<int> ret;int ret1 = nums[left];int ret2 = nums[right];if (nums[left] + nums[right] == -nums[i]){ret.push_back(nums[left]);ret.push_back(nums[right]);ret.push_back(nums[i]);v.push_back(ret);}if (nums[left] + nums[right] < -nums[i]){while (nums[left] == ret1){left++;if (left >= right){break;}}}else{while (nums[right] == ret2){right--;if (left >= right){break;}}}}int j = nums[i];while (nums[i] == j){i++;if (i > nums.size() - 1){break;}}}return v;
}  
};

7.四数之和

题目:

在这里插入图片描述

输出样例和输入样例:

在这里插入图片描述

四数之和可以参照三数之和,题目也是类似,我们需要找到四个数的和等于目标的target,但是这四个数的位置不能是一样的,

解法一:暴力解法
暴力解法很简单,只需要用四层循环把每种情况进行遍历一遍即可,最后再用容器进行去重操作。

解法二:双指针
这里和三数取和类似,我们只需要固定第一个数,对后面的数进行三数求和,然后将第一个固定的数逐渐往后遍历即可。

代码展示:

class Solution 
{
public:vector<vector<int>> v;vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;//排序int n=nums.size();sort(nums.begin(),nums.end());for(int i=0;i<n;){for(int j=i+1;j<n;){int left=j+1,right=n-1;long long aim=(long long)target-nums[i]-nums[j];while(left<right){int sum=nums[left]+nums[right];if(sum<aim)left++;else if(sum>aim)right--;else{ret.push_back({nums[left++],nums[right--],nums[i],nums[j]});//nums和nums的去重while(left<right&&nums[left]==nums[left-1])left++;while(left<right&&nums[right]==nums[right+1])right--;}}j++;while(j<n&&nums[j]==nums[j-1])j++;}i++;while(i<n&&nums[i]==nums[i-1])i++;}return ret;}
};

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

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

相关文章

短剧app广告变现模式开发

短剧app搭建是一个涉及多个方面的复杂过程&#xff0c;下面将介绍主要的步骤和考虑因素&#xff1a; 明确目标和定位&#xff1a;在开始搭建之前&#xff0c;首先要明确你的目标受众是谁&#xff0c;以及短剧app的主要定位是什么。这有助于在后续的开发过程中更有针对性地进行…

医院运维团队需要具备的关键能力及产品推荐

为了实现医院一体化运维监控&#xff0c;医院运维团队需要具备以下关键能力&#xff1a; 1. 技术能力&#xff1a; 系统监控能力&#xff1a;运维团队需要熟练掌握各种监控工具&#xff0c;能够实时监控系统的运行状态&#xff0c;包括服务器性能、网络状况、应用程序运行情况…

window端口占用情况及state解析

背景&#xff1a; 在电脑使用过程中&#xff0c;经常会开许多项目&#xff0c;慢慢地发现电脑越来越卡&#xff0c;都不知道到底是在跑什么项目导致&#xff0c;于是就想查看一下电脑到底在跑什么软件和项目&#xff0c;以作记录。 常用命令 netstat -tuln &#xff1a; 使用…

Python3发送邮件如何添加附件?怎么配置?

Python3发送邮件的注意事项&#xff1f;如何配置Python3发邮件&#xff1f; Python3发送邮件时添加附件是一项常见的需求。无论是发送报告、图片&#xff0c;还是其他文件&#xff0c;掌握如何在邮件中添加附件至关重要。AokSend将详细介绍Python3发送邮件时如何添加附件&…

前端网站(二)-- 菜单页面【附源码直接可用】

菜单页面 开篇&#xff08;请大家看完&#xff09;&#xff1a;此网站写给挚爱&#xff0c;后续页面还会慢慢更新&#xff0c;大家敬请期待~ ~ ~ 轻舟所编写这个前端框架的设计初衷&#xff0c;纯粹是为了哄对象开心。除此之外&#xff0c;并无其它任何用途或目的。 此前端框…

JavaScript运行原理和执行过程

参考&#xff1a; https://www.cnblogs.com/hexrui/p/15939592.html 1、执行上下文栈&#xff08;调用栈&#xff09; GECGlobal Execution Context&#xff08;GEC&#xff09;被放入到ECS&#xff08;Execution Context Stack&#xff0c;简称ECS&#xff09;中 GEC开始执…

24面试记录002

文章目录 12、brpc的优化2.1 brpc网络有啥降级&#xff1f; 3、移动语义4、python协程 二、1. mq怎么保障数据的顺序&#xff1f;3.brpc中上下游通信&#xff0c;怎么测评新增字段大小&#xff0c;对耗时的影响&#xff1f; 1 1、brpc和grpc区别&#xff0c;为啥选择brpc? gr…

护眼灯哪些牌子好?一文刨析护眼灯怎么选择!

护眼灯哪些牌子好&#xff1f;护眼台灯作为对抗视力挑战的一种方法&#xff0c;逐渐赢得了众多家长的青睐。这些台灯利用尖端光学技术&#xff0c;发出柔和且无刺激的照明&#xff0c;有助于保护眼睛不受伤害。它们不但可以调节亮度和色温&#xff0c;打造一个舒适且自然的阅读…

2024-06-19 问AI: 在LLM中,RAG是什么?

文心一言 在LLM&#xff08;大型语言模型&#xff09;中&#xff0c;RAG全称为Retrieval-Augmented Generation&#xff0c;即检索增强生成。以下是关于RAG的详细解释&#xff1a; 基本概念&#xff1a; RAG是一种通过检索LLMs之外的数据源来支持其生成答案的技术。它结合了搜…

【字符串在Python中的应用】

字符串是Python中非常重要的数据类型&#xff0c;它们是一系列字符的集合。Python提供了丰富的字符串操作方法&#xff0c;可以方便地处理和操作字符串。以下是一些常见的字符串操作及其详细教程。 字符串的基本操作 1. 创建字符串 字符串可以用单引号 或双引号 " 包围…

upload-labs第十三关教程

upload-labs第十三关教程 第十三关一、源代码分析代码审计 二、绕过分析1&#xff09;0x00绕过a.上传eval.pngb.使用burpsuite进行拦截修改之前&#xff1a;修改之后&#xff1a;进入hex模块&#xff1a; c.放包上传成功&#xff1a; d.使用中国蚁剑进行连接 2&#xff09;%00绕…

【分布预测】DistPred:回归与预测的无分布概率推理方法

论文题目&#xff1a;DistPred: A Distribution-Free Probabilistic Inference Method for Regression and Forecasting 论文作者&#xff1a;Daojun Liang, Haixia Zhang&#xff0c;Dongfeng Yuan 论文地址&#xff1a;https://arxiv.org/abs/2406.11397 代码地址&#xff1a…

小白学RAG:大模型 RAG 技术实践总结

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 汇总合集…

C++之模板(三)

1、缺省模板参数 可以将数据结构类型传递进来&#xff0c;比如vectop<T>&#xff08;如果没传就是默认&#xff09; 把vector当作类型参数来传递&#xff0c;从而使用它的接口然后适配出新的接口。实际上这个Stack称为适配器。有时候可能需要vector&#xff0c;但是又需…

编程电脑怎么接网线:详细步骤与注意事项

编程电脑怎么接网线&#xff1a;详细步骤与注意事项 在数字化日益普及的今天&#xff0c;无论是编程工作还是日常使用&#xff0c;电脑连接网络都是必不可少的步骤。其中&#xff0c;通过网线连接网络以其稳定性和高速率而备受青睐。然而&#xff0c;对于许多非专业人士来说&a…

【Python】AJAX

AJAX基础 一、AJAX1.1 概述1.2 XMLHttpRequest对象1.3 AJAX请求六部曲1.4 图解AJAX请求步骤 二、jQuery与AJAX2.1 jQuery.get()2.2 jQuery.getJSON()2.3 jQuery.post()2.4 jQuery.ajax() 三、Django使用AJAX3.1 请求类型3.2 PUT与PATCH的区别3.3 接收及响应JSON3.3.1 接收JSON3…

前端-echarts5.0 tooltip不显示问题

在echarts官网完成绘制后&#xff0c;将配置复制到本地&#xff0c;结果tooltip无法显示。 项目使用vue3,echarts5.0,代码写的是vue2。 option {tooltip: {trigger: axis,axisPointer: {type: cross,crossStyle: {color: #999}}},legend: {data: [完工数量, 完成率]},xAxis:…

ui自动化selenium,清新脱俗代码,框架升级讲解

一&#xff1a;简化 1. 新建common 包 新建diver.py 封装浏览器驱动类 from selenium import webdriverclass Driver():"""浏览器驱动类定义 一个【获取浏览器驱动对象driver的方法】。支持多种类型浏览器"""def get_driver(self,browser_typ…

《扩散模型 从原理到实战》Hugging Face (三)

第四章 Diffusers 实战 安装Difffusers 库 pip install -qq -U diffusers datasets transformers accelerate ftfy pyarrow扩散模型调度器 from diffusers import DDPMScheduler noise_scheduler DDPMScheduler(num_train_timesteps1000)定义扩散模型 from diffusers impo…

二维码展示与下载

1. 二维码展示(前端展示) 基于vue-qr组件实现 下载vue-qr组件 npm install vue-qr --save页面vueqr组件导入 import VueQr from vue-qr;export default {components: {VueQr} }页面展示 <el