【算法】利用双指针法解决算法题(C++)

文章目录

  • 1. 前言
  • 2. 双指针法引入
    • 283.移动零
  • 3. 使用双指针法解决算法题
    • 1089.复写零
    • 202.快乐数
    • 11.盛最多水的容器
    • [611.有效三角 形的个数](https://leetcode.cn/problems/valid-triangle-number/description/)
    • LCR179.查找总价格为目标值的两个商品
    • 15.三数之和
    • 18.四数之和

1. 前言

  1. 双指针并非真正意义上的指针,实际上一般使用下标表示/代替。
  2. 通常用于处理数组或链表等数据结构。
  3. 主要思想是使用两个指针在数组或链表中进行迭代、比较或操作

2. 双指针法引入

我们直接通过下面一道例题进行双指针方法的引入

283.移动零

在这里插入图片描述

思路

  1. 如图所示,我们定义双指针,每次移动指针,数组都满足:
    • cur左侧元素为处理过的元素,右侧为未处理的元素
    • 处理后的元素分为非零元素和零
    • dest 左侧为非零元素,dest与cur之间为零
  2. 细节注意:
    • 由于cur从0开始遍历数组,将dest从-1位置开始
    • 如果cur当前元素不为0,则于dest位置元素交换(先进行++dest)

代码

void moveZeroes(vector<int>& nums) {// cur: 从左到右遍历数组,dest: 非零元素的最后一个位置// [0, dest] 非零 [dest+1, cur-1] 零 [cur+1, n-1] 未处理int cur = 0, dest = -1;for(cur = 0; cur < nums.size(); ++cur){if(nums[cur] != 0)std::swap(nums[++dest], nums[cur]);}
}

3. 使用双指针法解决算法题

1089.复写零

在这里插入图片描述

思路

  1. 题目要求复写数组中的0,且数组长度不变(且应就地修改数组)
  2. 此时我们思考:
    • 当从前向后复写的时候,如1023 -> 1002,此时就会发生问题,当我们将0复写到2的位置后,2已经被覆盖,后续找不到该元素了
    • 此时我们可以尝试进行从后向前复写
  3. 当决定从后向前复写的操作,此时思路步骤如下图所示:
    在这里插入图片描述

代码

void duplicateZeros(vector<int>& arr) {int cur = 0, dest = -1;int n = arr.size();// 先找到最后一个复写的数while(dest < n - 1){// num[cur] 不为0,cur,dest后移一步,为零cur后移一步dest后移两步if(arr[cur] != 0)  dest++;else    dest += 2;if(dest >= n-1) break;cur++;}// 处理边界情况 如[1, 0, 2, 3, 0, 4]if(dest == n){arr[n-1] = 0;dest-=2, cur--;}// 从后向前复写while(cur >= 0){if(arr[cur] != 0){arr[dest--] = arr[cur];}else{   // nums[cur] 为零,复写两次arr[dest--] = 0;arr[dest--] = 0;}cur--;}
}

202.快乐数

在这里插入图片描述

思路

在这里插入图片描述

  1. 我们首先通过上面的方式有了判断快乐数的方法,即一直算平方和看最后成环是否有1
  2. 对于此类成环问题,如判断链表是否有环,我们采用快慢指针来解决:
    • 如果快慢指针相遇,如果相遇位置不等于1,则不是快乐数
  3. 由此我们可以编写代码↓

代码

  • 将求 每位平方和 的操作写位函数
  • 快慢指针的移动,即求每位平方和的次数:
    • 慢指针每次求一次,快指针每次求两次。
    • 当两指针相遇,通过判断某个指针是否为1返回最终结果。
// 求每位平方和
int bitSquare(int n)
{int sum = 0; while(n > 0){int bit = n % 10;sum += bit * bit;n /= 10;}return sum;
}bool isHappy(int n) {// 由已知得,数字一定成环,当slow与fast相遇// 如果相遇位置值为1,则是快乐数int slow = n, fast = bitSquare(n);;while(slow != fast){// 慢指针每次一步,快指针每次两步slow = bitSquare(slow);fast = bitSquare(bitSquare(fast));}return fast == 1;
}

11.盛最多水的容器

在这里插入图片描述

思路

  • 解法一:暴力解法:

    在这里插入图片描述

  • 解法二:利用单调性,使用双指针法

    在这里插入图片描述

  • 有了双指针法的前提算法思想,我们可以总结出步骤:

    • 每次统计以left,right为边界的容量,并移动值更小的指针,重复步骤直到指针相遇

代码

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]){right--;}else{left++;}}return ret;
}

611.有效三角 形的个数

在这里插入图片描述
思路

  • 解法一:暴力枚举
    • 用三个for循环计算更新结果,时间开销太大!
for(int i = 0; i < n-1; ++i)for(int j = i + 1; j < n - 1; ++j)for(int k = j + 1; k < n - 1; ++k)check(i, j, k); // 省略具体步骤
  • 解法二:根据单调性用双指针法

    1. 我们知道三条边构成三角形的条件是 任意两条边之和>第三条边
    • 而对于a <= b <= c 的三条边来说,由于a+c > b, b + c > a恒成立,我们只需判断a + b > c 是否成立即可 (注意下图思路中,我们用下标表示其在数组中的值)

    在这里插入图片描述
    2. 则思路如下:

    • 排序数组
    • 通过外层for循环固定最大边,内层while循环双指针找满足条件的边

代码

// 给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数
int triangleNumber(vector<int>& nums) {std::sort(nums.begin(), nums.end()); // 先排序数组int n = nums.size(), count = 0;// 外层for循环 控制最大数for(int i = n - 1; i >= 1; --i){int left = 0, right = i - 1;while(left < right){ if(nums[left] + nums[right] <= nums[i])   left++;else { // 如果 left + right > 最大数,则区间内的所有left与right组合均满足三角形count += right - left;right--;}}}return count;
}

LCR179.查找总价格为目标值的两个商品

在这里插入图片描述

思路

  • 解法一:暴力枚举
    • 通过两层for循环,判断是否满足条件
    • 时间开销大,O(n^2)
for(int i = 0; i < n; ++i)for(int j = i + 1; j < n - 1; ++j)check(nums[i] + nums[j] == target);
  • 解法二:利用单调性使用双指针法
    1. 题目要求找到数组中任意两个和为target的数: 在这里插入图片描述

代码

vector<int> twoSum(vector<int>& price, int target) {int n = price.size();int left = 0, right = n - 1;while(left < right){int sum = price[left] + price[right]; // 记录两数和if(sum < target)left++;else if (sum > target)right--;else return {price[left], price[right]};}return {};
}

15.三数之和

在这里插入图片描述

思路

  1. 题目要求找到数组中三个不同的位置,满足nums[i] + nums[j] + nums[k] == 0,并找到所有满足条件的三元组:
  • 解法一:排序+暴力枚举+使用set去重

  • 解法二:排序+双指针法
    在这里插入图片描述

代码

vector<vector<int>> threeSum(vector<int>& nums) {// 排序数组 便于后面去重等std::sort(nums.begin(), nums.end());int n = nums.size();vector<vector<int>> ret;// for循环固定第一个数,则固定的数后的序列进行“找和为target的两个数”// 如果 i = -5, 则target = 5for(int i = 0; i < n - 2; ++i){// 如果固定的数重复,跳过重复的数if(i > 0 && nums[i] == nums[i-1])    continue;int left = i + 1, right = n - 1;while(left < right){int sum = nums[i] + nums[left] + nums[right];if(sum < 0){left++;}   else if(sum > 0){right--;}else // 找到满足的数,插入到ret中,并更新left和right{ret.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--;}left++, right--;}}}return ret;
}

18.四数之和

在这里插入图片描述

思路

  1. 分析题目,题目要求得到 a + b + c + d = target,当我们固定了两个数后,只需判断 c + d = targer - a - b 即可。
  2. 该题与三数之和的解法如出一辙:
  • 排序+双指针法
    • 先通过两个for循环分别固定前两位数
    • 随后通过双指针法进行比较移动

代码

vector<vector<int>> fourSum(vector<int>& nums, int target) {// 1. 排序std::sort(nums.begin(), nums.end());vector<vector<int>> ret; // 结果数组int n = nums.size();// 固定第一个数for(int i = 0; i < n; ++i){if(i > 0 && nums[i] == nums[i - 1]) continue; // i跳过重复数// for循环内即 “三数之和” 的代码// 固定第二个数for(int j = i + 1; j < n; ++j){if(j > i + 1 && nums[j] == nums[j - 1]) continue; // j 跳过重复数int left = j + 1, right = n - 1;long long _target = target - (long long)nums[i] - (long long)nums[j]; // 内层循环的_target为减去i和j的值while(left < right){long long sum = (long long)nums[left] + (long long)nums[right]; // sum值与_target比较if(sum > _target)   right--;else if(sum < _target) left++;else{ret.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--;left++, right--;}}}}return ret;
}

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

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

相关文章

redis基本用法学习(C#调用StackExchange.Redis操作redis)

StackExchange.Redis是基于C#的高性能通用redis操作客户端&#xff0c;也属于常用的redis客户端之一&#xff0c;本文学习其基本用法。   新建Winform项目&#xff0c;在Nuget包管理器中搜索并安装StackExchange.Redis&#xff0c;如下图所示&#xff1a;   StackExchange.…

开发利器——C语言必备实用第三方库

​ 对于广大C语言开发者来说&#xff0c;缺乏类似C STL和Boost的库会让开发受制于基础库的匮乏&#xff0c;也因此导致了开发效率的骤降。这也使得例如libevent这类事件库&#xff08;基础组件库&#xff09;一时间大红大紫。 今天&#xff0c;码哥给大家带来一款基础库&#…

JavaScript进阶(事件+获取元素+操作元素)

目录 事件基础 事件组成 执行事件的步骤 获取元素 根据ID获取元素 根据标签名获取元素 获取ol中的小li 类选择器&#xff08;html5新增的I9以上支持&#xff09; 获取body和html 操作元素 innerText和innerHtml 表单标签 样式属性操作 操作元素总结 事件基础 事…

JY901S 9轴姿态角度传感器模块

JY901S 9轴姿态角度传感器模块 JY901S 简介模块特性引脚说明IIC通讯IIC读写寄存器代码示例 JY901S 简介 模块集成高精度的陀螺仪、加速度计、地磁场传感器&#xff0c;采用高性能的微处理器和先进的动力学解算与卡尔曼动态滤波算法&#xff0c;能够快速求解出模块当前的实时运…

基于Python的新能源汽车销量分析与预测系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 基于Python的新能源汽车销量分析与预测系统是一个使用Python编程语言和Flask框架开发的系统。它可以帮助用户分析和预测新能源汽车的销量情况。该系统使用了关系数据库进行数据存储&#xff0c;并…

MATLAB信号处理与应用 读书笔记 一

完成了基本操作&#xff0c;今天组数也正常&#xff0c;需要对应解决fsctrl文件中的信号处理相关 重点关注4傅里叶变换&#xff0c;6FIR滤波器&#xff0c;10信号处理中的应用字符的链接[aa,bb]&#xff1b; N18;N216; n0:N-1;k10:N1-1;k20:N2-1; w2*pi*(0:2047)/2048; Xw(1-…

【并发设计模式】聊聊 基于Copy-on-Write模式下的CopyOnWriteArrayList

在并发编程领域&#xff0c;其实除了使用上一篇中的属性不可变。还有一种方式那就是针对读多写少的场景下。我们可以读不加锁&#xff0c;只针对于写操作进行加锁。本质上就是读写复制。读的直接读取&#xff0c;写的使用写一份数据的拷贝数据&#xff0c;然后进行写入。在将新…

CSS5 | CSS滑动门左扇与右扇图片重叠问题解决

本文中所使用的滑动门背景图片是自己用微软相册手工切的&#xff0c;没用ps&#xff0c;所以凑乎看吧 首先放出一张目标效果也是最终完成图 下面说问题 CSS推拉门原理 按原理来说&#xff0c;就是两个行内块前后站一行&#xff0c;然后前面的a标签和span标签分别是推拉门素材…

TCP 三次握手:四次挥手

TCP 三次握手/四次挥手 TCP 在传输之前会进行三次沟通&#xff0c;一般称为“三次握手”&#xff0c;传完数据断开的时候要进行四次沟通&#xff0c;一般称为“四次挥手”。 数据包说明 源端口号&#xff08; 16 位&#xff09;&#xff1a;它&#xff08;连同源主机 IP 地址…

验证码服务使用指南

验证码服务使用指南 1 部署验证码服务 1.1 基础环境 Java 1.8 Maven3.3.9 1.2 安装Redis 参考“Redis安装指南” 1.3 部署验证码服务 1.3.1 下载源码 使用git从远程下载验证码服务代码(开源)。 1.3.2 使用idea打开项目 使用idea打开上一步下载的sailing目录&#xf…

优维产品最佳实践第20期:控制台全链路监控

之前我们会觉得cmdb自动发现没有上报很难排查&#xff0c;弄不清楚数据的上报链路&#xff1b;监控指标的数据断点很难定位&#xff0c;flink对现场来说是一个黑盒子&#xff1b;apm数据更新不及时到底是上报异常还是入库失败呢&#xff1f; 现在控制台集成了对数据链路的监控…

超声系统前端理论与模拟仿真-续

作者&#xff1a;蒋志强 本人同意他人对我的文章引用&#xff0c;但请在引用时注明出处&#xff0c;谢谢&#xff0e;作者&#xff1a;蒋志强 前言 近期整理了一下彩超前端及波束合成相关的内容&#xff0c;很早以前已经有过一次&#xff0c;这次把其它的内容总结一下&#xf…

【数据结构】什么是二叉树?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;二叉树的定义 &#x1f4cc;二叉树的特点 &#x1f4cc;特殊二叉树 &#x1f4cc;二叉树的性质 &#x1f4cc;二叉树的存储结构 &#x1f4cc;二叉树…

【Vue3+TypeScript】快速上手_代码

目录 001_写一个APP组件 002_一个简单的效果 003_setup概述 004_setup的返回值 005_setup与Options API的关系 006_setup语法糖 007_ref创建_基本类型的响应式数据 008_reactive创建_对象类型的响应式数据 009_ref创建_对象类型的响应式数据 010_ref对比reactive 01…

(2023|CVPR,Corgi,偏移扩散,参数高斯分布,弥合差距)用于文本到图像生成的偏移扩散

Shifted Diffusion for Text-to-image Generation 公众&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 或直接进 Q 交流群&#xff1a;922230617 获取资料&#xff09; 目录 0. 摘要 1. 简介 2. 方法 2.1 偏移扩散 3. 实验 3.1 无监督文本到图像生成 3.2 无…

三相电机转差率为负值的情形

1.电机开始发电的特征 注意&#xff0c;电机因为有输入频率对原始旋转磁场的影响&#xff0c;在正常工作时&#xff0c;应该处于稳态&#xff0c;因为旋转磁场决定了这个系统的运转方向和运转的大致频率区间。它会处于力矩平衡态。但是&#xff0c;如果&#xff0c;此时电机处…

网络安全保障领域

计算机与信息系统安全---最主要领域 云计算安全 IaaS、PasS、SaaS(裸机&#xff0c;装好软件的电脑&#xff0c;装好应用的电脑) 存在风险&#xff1a;开源工具、优先访问权、管理权限、数据处、数据隔离、数据恢复、调查支持、长期发展风险 云计算安全关键技术&#xff1a;可信…

Vue-Pinina基本教程

前言 官网地址&#xff1a;Pinia | The intuitive store for Vue.js (vuejs.org) 看以下内容&#xff0c;需要有vuex的基础&#xff0c;下面很多概念会直接省略&#xff0c;比如state、actions、getters用处含义等 1、什么是Pinina Pinia 是 Vue 的存储库&#xff0c;它允许您跨…

〖Python网络爬虫实战㊹〗- JavaScript Hook 的用法

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;订阅本专栏前必读关于专栏〖Python网络爬虫实战〗转为付费专栏的订阅说明作者&#xff1…

buuctf-Misc 题目解答分解97-99

97.[BSidesSF2019]zippy 下载完就是一个流量包 追踪tcp nc -l -p 4445 > flag.zip unzip -P supercomplexpassword flag.zip Archive: flag.zip 压缩包密码 supercomplexpassword 保存为 flag.zip 解压得到flag 98.[GUET-CTF2019]虚假的压缩包 先从虚假的压缩包入手 &am…