算法:双指针解决数组划分和数组分块问题

文章目录

  • 实现原理
  • 实现思路
  • 典型例题
    • 移动0
    • 复写0
    • 快乐数
    • 盛最多水的容器
    • 有效三角形的个数
    • 三数之和
    • 四数之和
  • 总结

在快速排序或者是其他和数组有关的题目中,有很经典的一类题目是关于数组划分的,数组划分就是把数组按照一定的规则划分为不同的区间,使得达到某种目的

首先先看实现的原理是什么

实现原理

两个指针的作用?

cur:从左向右扫描数组,遍历数组

dest:已处理的区间内,非零元素的最后一个位置

数组划分就是把数组划分成三个区间:

[0,dest][dest+1,cur-1][cur,n-1]

而这三个区间就对应到了题目要求的区间,假设现在有这样的题目

在这里插入图片描述
那经过区间划分,就可以把[0,dest]划分为非0的区域,[dest+1,cur-1]划分为只有0的区间,而剩下的就是待处理的区间

实现思路

有了上面的理论基础,实现思路就简单多了:

我们让cur从前向后遍历,如果cur遇到0元素,就让cur++,因为cur相当于是一个用来探路的指针,而dest的作用就是用来进行区间的划分,于是根据这个原理,当cur遇到了非0的元素时,就让dest++再让curdest这两个位置的元素进行一次交换,交换后的结果就达到了,把非0元素交换到前面,0元素换到后面的结果

技巧

永远让dest初始值为-1,cur的初始值为0,并且让cur最后更变值,可以有效处理掉很多越界问题

典型例题

先看几个简单的题目熟悉这个算法的思路:

移动0

在这里插入图片描述

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

看上述代码,就严格执行了刚才代码的思路

  1. 如果cur遇到0元素,就让cur++

  2. cur遇到了非0的元素时,就让dest++再让curdest这两个位置的元素进行一次交换


复写0

在这里插入图片描述

既然通篇介绍的主要是双指针算法,那这个题也要使用双指针的基本原理

如果此题可以创建多个数组,那么创建一个数组,上面一个指针控制原数组,下面控制新数组,如果是0就填入两个0,如果不是0就填入该数字,到达对应数量就不再进行写入数据

但这里要求不能够使用额外的空间,因此就要把异地的双指针变成原地双指针

那原地双指针如何实现?

首先,原地的双指针问题在于,如果从前向后遍历,当遍历到的数字是0,写入两个0后,原来的数据就被覆盖掉了,这样就会一直进行0的循环,因此解决方案就是从后向前遍历

那么接下来思考如何遍历?一个从最后开始,那另外一个?显然,这是下一个需要解决的问题,另外一个部分应该从哪里开始

在这里插入图片描述
通过这里画图也能看出,cur的位置其实就是复制结束后的位置,那么这个位置的寻找过程就是下一步要进行的问题

cur应该如何寻找?其实又可以演化为双指针的问题,从开始找,当遇到0就向后走两次,遇到非0就走一次,那么这样就可以找到cur

这样的思路是没有问题的,但是也有特殊情况,如果最后元素是0,那dest向后走两步不就越界了吗?因此这里也需要对边界做特殊处理,如果边界为0,那么就让最后输出的destcur向前一步走即可避免越界的情况出现

class Solution 
{
public:void duplicateZeros(vector<int>& arr) {int cur=0,dest=-1,n=arr.size();while(cur<n){if(arr[cur]==0){dest+=2;}else{dest++;}if(dest>=n-1){break;}cur++;}if(dest==n){arr[n-1]=0;cur--;dest-=2;}while(cur>=0){if(arr[cur]==0){arr[dest--]=arr[cur];arr[dest--]=arr[cur--];}else{arr[dest--]=arr[cur--];}}}
};

快乐数

在这里插入图片描述

这个题思路也很奇特,先模拟一下实现的流程:

情景1:

在这里插入图片描述

情景2:

在这里插入图片描述
此时对题意就有了基本了解,那么这个图其实和链表中的环形链表很相似,我们其实就可以把他抽象成一个环形链表的相遇问题,当相遇的时候,如果对应的值不是1,那么就证明这里并不是快乐数,相反就是快乐数

因此这个题就很好解决了,本质上这个原理和环形链表的快慢指针的过程是一样的

class Solution 
{
public:int CalRes(int num){int res = 0;while (num){res += (num % 10) * (num % 10);num = num / 10;}return res;}bool isHappy(int n) {int num = n;int slow = CalRes(num);int fast = CalRes(CalRes(num));while (slow != fast){slow = CalRes(slow);fast = CalRes(CalRes(fast));}if (fast == 1){return true;}else{return false;}}
};

盛最多水的容器

在这里插入图片描述

本题设计也很巧妙,但依旧是利用双指针来解决,知道了双指针的解决原理后解决并非难事

一个从左走 一个从右走 根据木桶效应,计算出结果后要舍弃小的部分,继续向内遍历,使得最终时间复杂度控制在O(N)内

class Solution 
{
public:int maxArea(vector<int>& height){int left = 0;int right = height.size() - 1;int v = 0;int max = 0;while (left < right){v = min(height[left], height[right]) * (right - left);if (v > max){max = v;}if (height[left] < height[right]){left++;}else{right--;}}return max;}
};

有效三角形的个数

在这里插入图片描述

看到这个题,第一思路是直接暴力枚举三次for循环,直接找,但最后是通过不了的,时间复杂度过高了,因此这里还是使用双指针的解法,但是要利用单调性进行解决

首先,对于三个数字我们要进行判断的时候,如果这个数字是单调排序的,比如这里是升序排序,那么只需要判断前两个数相加的和大于第三个数即可,因此根据这个原理,我们就可以采取下面的思维方式

依据单调性采用双指针解决问题

这个算法的思路就是,先固定右边最大的数字作为最大的数,倒数第二大的数字为right,左边的数为left

如果此时left+right>固定,那么此时left右边的数同样符合条件,那么只需要right--即可

如果此时left+right<固定,那么此时left后面的数也不符合要求,就让left++

循环结束后,再通过挪动右边的数进行循环,这样时间复杂度在O(N^2)的基础上就解决了这个问题

class Solution 
{
public:int triangleNumber(vector<int>& nums) {sort(nums.begin(),nums.end());int cut=0;for(int max=nums.size()-1;max>=2;max--){int left=0,right=max-1;while(left<right){if(nums[left]+nums[right]>nums[max]){cut+=right-left;right--;}else{left++;}}}return cut;}
};

三数之和

在这里插入图片描述

此题难度在于代码实现的细节处理和去重的问题上

代码思路有两数之和的思路铺垫,整体难度不大,控制住其中一个进行另外两个数据的相加即可,但在边界的处理上需要进行一些操作

最后是去重的操作,去重的操作是很重要的一步,细节很多,要处理区间内的去重和区间外单独的去重

vector<vector<int>> threeSum(vector<int>& nums) 
{vector<vector<int>> v;// 排序sort(nums.begin(),nums.end());for(int i=0;i<nums.size();){// 优化if(nums[i]>0){break;}int left=i+1,right=nums.size()-1;while(left<right){if(nums[left]+nums[right]+nums[i]>0){right--;}else if(nums[left]+nums[right]+nums[i]<0){left++;}else{v.push_back({nums[i],nums[left],nums[right]});left++;right--;// 去重1while(left<right && nums[left]==nums[left-1]){left++;}while(left<right && nums[right]==nums[right+1]){right--;}}}       // 去重2i++;while(i<nums.size() && nums[i]==nums[i-1]){i++;}}return v;
}

四数之和

在这里插入图片描述

代码思路和三数之和基本相同,注意long long问题即可

class Solution 
{
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> v;// 排序sort(nums.begin(),nums.end());// 控制第一个数for(int i=0;i<nums.size();){// 控制第二个数for(int j=i+1;j<nums.size();){// 在区间内找int left=j+1,right=nums.size()-1;while(left<right){long long tmp=(long long)nums[i]+nums[j]+nums[left]+nums[right];if(tmp>target){right--;}else if(tmp<target){left++;}else{v.push_back({nums[i],nums[j],nums[left],nums[right]});left++;right--;// 去重while(left<right && nums[left]==nums[left-1]){left++;}while(left<right && nums[right]==nums[right+1]){right--;}}}// 对固定数去重j++;while(j<nums.size() && nums[j]==nums[j-1]){j++;}}i++;while(i<nums.size() && nums[i]==nums[i-1]){i++;}}return v;}
};

总结

双指针问题是入门算法题,但却有很大的使用场景,具体来说,当要进行数组划分和数组分块的时候,可以选择先进行排序,进行有序数组

对于有序数组来说,利用单调性解决问题是常见的手段,在实际应用题目中有很大的利用价值

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

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

相关文章

【【典型电路设计之片内存储器的设计之RAM的Verilog HDL描述一】】

典型电路设计之片内存储器的设计之RAM的Verilog HDL描述一 RAM是随机存储器&#xff0c;存储单元的内容可按需随意取出或存入。这种存储器在断电后将丢失所有数据&#xff0c;一般用来存储一些短时间内使用的程序和数据。 其内部结构如下图所示&#xff1a; 例&#xff1a;用…

PL 侧驱动和fpga 重加载的方法

可以解决很多的问题 时钟稳定后加载特定fpga ip &#xff08;要不内核崩的一塌糊涂&#xff09;fpga 稳定复位软件决定fpga ip 加载的时序 dluash load /usr/local/scripts/si5512_setup.lua usleep 30 mkdir -p /lib/firmware cp -rf /usr/local/firmare/{*.bit.bin,*.dtbo} …

拥塞控制(TCP限制窗口大小的机制)

拥塞控制机制可以使滑动窗口在保证可靠性的前提下&#xff0c;提高传输效率 关于滑动窗口的属性以及部分机制推荐看TCP中窗口和滑动窗口的含义以及流量控制 拥塞控制出现的原因 看了上面推荐的博客我们已经知道了&#xff0c;由于接收方接收数据的能力有限&#xff0c;所以要通…

LLM架构自注意力机制Transformers architecture Attention is all you need

使用Transformers架构构建大型语言模型显著提高了自然语言任务的性能&#xff0c;超过了之前的RNNs&#xff0c;并导致了再生能力的爆炸。 Transformers架构的力量在于其学习句子中所有单词的相关性和上下文的能力。不仅仅是您在这里看到的&#xff0c;与它的邻居每个词相邻&…

Go语言入门指南:基础语法和常用特性(下)

上一节&#xff0c;我们了解Go语言特性以及第一个Go语言程序——Hello World&#xff0c;这一节就让我们更深入的了解一下Go语言的**基础语法**吧&#xff01; 一、行分隔符 在 Go 程序中&#xff0c;一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ;…

Ajax介绍

1.与服务器进行数据交换&#xff1a;通过 Ajax 可以给服务器发送请求&#xff0c;并获取服务器响应的数据。 2.异步交互&#xff1a;可以在 不重新加载整个页面 的情况下&#xff0c;与服务器交换数据并 更新部分网页 的技术&#xff0c;如&#xff1a; 搜索联想、用户名是否可…

ChatGpt开源项目完美运行配置-ChatGml2

&#xff08;以下所有软件均可免费在网盘获取。&#xff09; 任务描述 本节任务是安装和配置chatgpt项目所需的软件以及chatgpt项目所需要的python库包&#xff0c;同时编写python代码来完成chatgpt项目的人机对话功能。 实验工具 显卡GTX1070&#xff08;专用内存需要大于等…

[JavaWeb]【五】web后端开发-Tomcat SpringBoot解析

目录 一 介绍Tomcat 二 基本使用 2.1 解压绿色版 2.2 启动TOMCAT 2.3 关闭TOMCAT 2.4 常见问题 2.5 修改端口号 2.6 部署应用程序 三 SpringBootWeb入门程序解析 前言&#xff1a;tomcat与SpringBoot解析 一 介绍Tomcat 二 基本使用 2.1 解压绿色版 2.2 启动TOMCAT 2…

BOXTRADE-天启量化分析平台 系统功能预览

BOXTRADE-天启量化分析平台 系统功能预览 系统功能预览 1.登录 首页 参考登录文档 2. A股 行情与策略分析 2.1 A股股票列表 可以筛选和搜索 2.2 A股行情及策略回测 2.2.1 行情数据提供除权和前复权&#xff0c;后复权数据&#xff1b;外链公司信息 2.2.2 内置策略执行结果…

matlab 检测点云中指定尺寸的矩形平面

目录 一、概述1、算法概述2、主要函数二、代码示例三、结果展示四、参数解析输入参数名称-值对应参数输出参数五、参考链接一、概述 1、算法概述 detectRectangularPlanePoints:检测点云中指定尺寸的矩形平面 <

git快速入门

常用指令 git config --global user.email yinweitingptxinke.com git config --global user.name yinweiting git commit -m "日志消息" filename git reflog 查看精简版本信息 git log 查看详细版本信息 注意:这里设置的用户签名和将来登录的GitHub的账号没有任…

【HCIP】01.RSTP

STP的缺点 STP对计时器的依赖&#xff08;需要等固定30s&#xff09;STP重收敛速度过程慢&#xff08;30s或50s&#xff09;拓扑变化机制太慢&#xff08;高度中央集权&#xff0c;需要上报到根桥再下发TCN&#xff09; RSTP 802.1W&#xff0c;收敛速度更快&#xff0c;能够…

Python程序设计基础:random库的使用

文章目录 一、常见的random库函数二、应用实例 一、常见的random库函数 在使用Python语言进行编程计算时&#xff0c;计算机完成的计算主要是确定的&#xff0c;但是在将其进行应用时&#xff0c;人们会模拟现实生活中的现象和活动&#xff0c;希望其增加一些随机性&#xff0…

econml介绍

EconML简介 EconML: A Python Package for ML-Based Heterogeneous Treatment Effects Estimation EconML是一个通过机器学习方法从观察数据中估计heterogeneous treatment effects的Python包。该软件包是微软研究院ALICE项目的一部分&#xff0c;目的是将最新的机器学习方法…

神经网络改进:注重空间变化,权重参数调整,正则化, 熵的简单理解

目录 神经网络改进&#xff1a;注重空间变化 将高纬空间映射到地位空间便于表示&#xff08;供给数据&#xff09; 将地位空间映射到高纬空间进行分类聚合&#xff08;达到可分状态&#xff08;K-means&#xff09;&#xff09; 神经网络改进&#xff1a;权重参数调整 自注…

【RabbitMQ】RabbitMQ整合SpringBoot案例

文章目录 1、前情提要【RabbitMQ】2、RabbitMQ-SpringBoot案例 -fanout模式2.1 实现架构总览2.2 具体实现2.2.1生产者2.2.1消费者 1、前情提要【RabbitMQ】 【RabbitMQ】消息队列-RabbitMQ篇章 RabbitMQ实现流程 2、RabbitMQ-SpringBoot案例 -fanout模式 2.1 实现架构总览…

maven打出jar中动态替换占位符

使用场景&#xff1a; maven打出的jar中pom.xml动态替换占位符 有些时候某些公共工具jar包被项目引用后发现公共jar的pom.xml中的version依然还是占位符&#xff0c;例如下面 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok<…

相关搜索引擎常用搜索语法(Google hacking语法和FOFA语法)

一&#xff1a;Google Hack语法 Google Hacking原指利用Google搜索引擎搜索信息来进行入侵的技术和行为&#xff0c;现指利用各种搜索引擎并使用一些高级的搜索语法来搜索信息。既利用搜索引擎强大的搜索功能&#xff0c;在在浩瀚的互联网中搜索到我们需要的信息。 &#xff0…

STM32 CubeMX (第二步Freertos任务通信:队列、信号量、互斥量,事件组,任务通知)

STM32 CubeMX STM32 CubeMX ____Freertos任务通信&#xff1a;队列、信号量、互斥量&#xff0c;事件组&#xff0c;任务通知 STM32 CubeMX一、STM32 CubeMX设置时钟配置HAL时基选择TIM1&#xff08;不要选择滴答定时器&#xff1b;滴答定时器留给OS系统做时基&#xff09;使用…

基于docker搭建owncloud Harbor 构建镜像

环境介绍&#xff1a;ContenOS7.9 docker17.12.1-ce 使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 docker pull owncloud #拉取镜像 docker pull mysql5.6 创建容器 docker run --name owncloud-mysql -p 3306:3306 -e MYSQL\_ROOT\_PASSWORDroot …