【c++算法篇】双指针(下)

🔥个人主页:Quitecoder
🔥专栏:算法笔记仓

朋友们大家好啊,本篇文章我们来到算法的双指针的第二部分
目录
- `1.有效三角形的个数`
- `2.查找总价格为目标值的两个商品`
- `3.三数之和`
- `4.四数之和`
- 5.双指针常见场景总结
1.有效三角形的个数
题目链接:611. 有效三角形的个数
题目描述:
这道题当然可以暴力求解,三层循环枚举所有情况,来进行判断,但是可以进行优化:
我们知道,三角形的满足条件是任意的两边之和大于第三边,但是如果我们已经判断了较小的两个边大于第三边,就不需要再进行剩下两组的判断,所以我们先进行排序,再进行枚举:
class Solution {
public:int triangleNumber(vector<int>& nums) {sort(nums.begin(),nums.end());}
};
具体讲解一下我们的思路:
这里使用的是一种双指针技术:固定最长的边(也就是数组中的最大值),使用两个指针来查找剩余部分中可能的两个较短边。如果找到了两个较短边的长度和大于最长边,那么这三者能构成一个三角形
class Solution {
public:int triangleNumber(vector<int>& nums) {sort(nums.begin(),nums.end());int count=0;for(int i=nums.size()-1;i>=2;i--){int lat=i-1,pre=0;while(pre<lat){if(nums[pre]+nums[lat]>nums[i]){count+=lat-pre;lat--;}else pre++;}}return count;}
};
它利用了一个重要的性质:如果你有三条边长分别为 a, b 和 c,且 a ≤ b ≤ c,那么 a, b 和 c 可以构成一个三角形当且仅当 a + b > c
步骤如下:
- 对数组
nums进行升序排序 - 初始化计数器
count为 0 - 从后往前遍历数组(从最大值开始,下标为
i),我们将这个值作为潜在的最长边c - 对于每一个
c,设置两个指针:pre指针指向数组的开始(下标为 0),lat指针指向c之前的元素(下标为i - 1) - 当
pre指针小于lat指针时:- 计算
nums[pre]和nums[lat]的和,将这个和与nums[i](也就是当前的c)进行比较 - 如果
nums[pre] + nums[lat] > nums[i],由于数组已经排序,所有在pre和lat之间的元素与nums[lat]的和都会大于nums[i],所以我们可以将lat - pre个三角形加到count上 - 然后将
lat向左移动一位(减小一点以寻找下一个可能的三角形) - 如果和小于等于
nums[i],我们将pre向右移动一位(增大一点以寻找可能的三角形)
- 计算
- 当处理完所有的
c后,返回count作为结果
本道题还是很简单的
2.查找总价格为目标值的两个商品
题目链接:LCR 179.查找总价格为目标值的两个商品
题目描述:
算法的具体思路:
- 初始化两个指针,
pre指向数组的开始(索引 0),last指向数组的末尾(索引price.size() - 1)
vector<int> s1;
int last=price.size()-1;
int pre=0;
-
进行一个
while循环,在数组两端移动pre和last指针直到它们相遇。循环的条件是pre < last,确保没有重复使用相同的元素。 -
在每次循环中,计算两个指针指向的数的和,判断这个和与目标值
target的关系:- 如果和大于
target,那么为了减小和,last指针左移(减小索引值) - 如果和小于
target,那么为了增大和,pre指针右移(增加索引值) - 如果和等于
target:- 将这两个数添加到结果
vectors1中。 - 因为只需要一组解,所以找到一对满足条件的数之后,通过
break语句退出循环
- 将这两个数添加到结果
- 如果和大于
while(pre<last)
{if(price[pre]+price[last]>target)last--;else if(price[pre]+price[last]<target)pre++;else {s1.push_back(price[pre]);s1.push_back(price[last]);break;}
}
- 返回结果
vector。如果找到至少一对和为target的数,s1会包含这两个数。如果没有找到,s1将是空的
完整代码如下:
class Solution {
public:vector<int> twoSum(vector<int>& price, int target) {vector<int> s1;int last=price.size()-1;int pre=0;while(pre<last){if(price[pre]+price[last]>target)last--;else if(price[pre]+price[last]<target)pre++;else {s1.push_back(price[pre]);s1.push_back(price[last]);break;}}return s1;}
};
3.三数之和
题目链接:15.三数之和
题目描述:
对于三数之和,我们大思路如下:
对于示例
我们首先进行排序:

然后,首先固定第一个数,只需要在后面的数中找到两个数使三个数相加和为0即可
对于后面的数的寻找,我们可以设置前后指针,如果三数之和大于零,则让较大的数减小点,即右指针左移,三数之和小于零,则让左指针右移,如果等于零,则讲这三个数据插入到目标数组中继续遍历
注意,上面的{-1,0,1}这三个数是可以构成目标数的,但是必须跳过其中一个-1,因为不能重复
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> result;sort(nums.begin(),nums.end());for(int i=0;i<nums.size()-2;i++){if(i>0&&nums[i-1]==nums[i])continue;int pre=i+1,las=nums.size()-1;while(pre<las){if(nums[pre]+nums[las]<(-nums[i]))pre++;else if(nums[pre]+nums[las]>(-nums[i]))las--;else{result.push_back({nums[i],nums[pre],nums[las]});while(pre<las&&nums[pre+1]==nums[pre])pre++;while(pre<las&&nums[las-1]==nums[las])las--;pre++;las--;}}}return result;}
};
注意的要点:
-
唯一性:返回的结果中不能包含重复的三元组。解决方法是在找到一个符合条件的组合后,跳过所有相同的元素
-
遍历策略:外层循环遍历数组,内层使用双指针从两端向中间查找两个其他元素,以保证三个数的和为零
-
跳过重复元素:
- 在外层循环中,如果当前的数字与前一个数字相同,则跳过以避免重复的三元组
for(int i=0;i<nums.size()-2;i++)
{if(i>0&&nums[i-1]==nums[i])continue;
- 在找到一个满足条件的三元组之后,同时跳过
pre指针的连续重复数字,并将pre指针向右移动 - 同样地,跳过
las指针的连续重复数字,并将las指针向左移动
-
寻找条件:三数之和等于零。这意味着在内层循环中,如果
nums[pre] + nums[las]小于-nums[i],则需要右移pre指针;如果大于-nums[i],则需要左移las指针;如果等于-nums[i],则记录该三元组,继续寻找其他可能的组合 -
边界条件:
- 外层循环的循环变量
i应小于nums.size() - 2,因为需要至少3个数来组成一个三元组 - 当
pre和las指针相遇时,内层循环结束。
- 外层循环的循环变量
我们还可以进一步优化,当i对应的数字大于零,意味着无论如何结果都大于零,就可以直接break了:
for(int i=0;i<nums.size()-2;i++)
{if(i>0&&nums[i-1]==nums[i])continue;if(nums[i]>0)break;
4.四数之和
题目链接:18.四数之和
题目描述:
这道题与上面三数求和大体思路一样,我们这次一次固定两个数,然后再遍历剩下的数,遇见相同的数就往后移动
注意
上道题数组长度是大于等于3的,而这道题nums数组长度大于等于1,意味着可能不存在四个数,所以首先我们先判断数组长度,如果小于四直接返回空数组
if(nums.size()<4)return{};
首先进行排序工作
接着开始完成函数内容,需要固定两个数,我们则需要嵌套两个循环,注意边界值即可:
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size()-3; i++) {if (i > 0 && nums[i] == nums[i - 1]) continue; for (int j = i + 1; j < nums.size()-2; j++) {if (j > i + 1 && nums[j] == nums[j - 1]) continue; ——————————
}
这里处理逻辑与上面一样,先跳过相同的数,在j的循环中,我们就进行和上面相同的操作了
int pre = j + 1;
int last = nums.size() - 1;
while (pre < last) {long long sum = (long long)nums[i] + nums[j] + nums[pre] + nums[last]; if (sum < target) {pre++;}else if (sum > target) {last--;}else {result.push_back({ nums[i], nums[j], nums[pre], nums[last] });while (pre < last && nums[pre] == nums[pre + 1]) pre++; while (pre < last && nums[last] == nums[last - 1]) last--; pre++;last--;}
}
本题还有一个关键点

它提供的值不一定是整形,所以上面函数中我们使用长整型来避免溢出
总代码如下:
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {if (nums.size() < 4)return{};vector<vector<int>> result;sort(nums.begin(), nums.end());for (int i = 0; i < nums.size() - 3; i++) {if (i > 0 && nums[i] == nums[i - 1]) continue; for (int j = i + 1; j < nums.size() - 2; j++) {if (j > i + 1 && nums[j] == nums[j - 1]) continue; int pre = j + 1;int last = nums.size() - 1;while (pre < last) {long long sum = (long long)nums[i] + nums[j] + nums[pre] + nums[last];if (sum < target) {pre++;}else if (sum > target) {last--;}else {result.push_back({ nums[i], nums[j], nums[pre], nums[last] });while (pre < last && nums[pre] == nums[pre + 1]) pre++;while (pre < last && nums[last] == nums[last - 1]) last--;pre++;last--;}}}}return result;}
};
5.双指针常见场景总结
双指针主要应用在有序数组或链表的问题中,以及一些可以通过前后关系来优化问题的场景:
-
有序数组的对撞指针:
- 两数之和:在有序数组中找到两个数,使它们的和为特定的目标值
- 三数之和/四数之和:与两数之和类似,但需要找到三个或四个数的组合
- 移除元素:从有序数组中移除重复项或特定值,并返回新数组的长度
-
快慢指针:
- 链表中环的检测:使用快慢指针检测链表是否有环,快指针一次移动两步,慢指针一次移动一步
- 寻找链表中点:使用快慢指针找到链表的中间节点,快指针结束时慢指针在中点
- 寻找链表的倒数第k个元素:快指针先移动k步,然后快慢指针共同移动,快指针到达末尾时慢指针所在位置即倒数第k个元素
-
前后指针:
- 归并排序中的合并步骤:使用两个指针分别指向两个有序数组的开始位置,以合并成一个新的有序数组。
- 对链表进行操作:在链表上进行操作时,如删除节点或反转链表,常常需要前后指针来保持结点的连接。
-
左右指针:
- 二分查找:在有序数组中查找元素,使用左右指针限定查找范围
双指针方法的关键在于,指针的移动可以依据问题的规律来减少不必要的比较或计算,从而提高算法效率。当然,双指针的使用需要充分理解问题的性质,并巧妙设计指针的移动策略。在很多问题中,双指针技术都能将时间复杂度从 O(n2) 优化到 O(n),超级好用
本节内容到此结束!!感谢大家阅读!!
相关文章:
【c++算法篇】双指针(下)
🔥个人主页:Quitecoder 🔥专栏:算法笔记仓 朋友们大家好啊,本篇文章我们来到算法的双指针的第二部分 目录 1.有效三角形的个数2.查找总价格为目标值的两个商品3.三数之和4.四数之和5.双指针常见场景总结 1.有效三角形…...
微图乐 多种装B截图一键制作工具(仅供娱乐交流)
软件介绍 采用exe进程交互通信。全新UI界面,让界面更加清爽简约。支持zfb、VX、TX、Yin行、Dai款、游戏等图片生成,一键超清原图复制到剪辑板,分享给好友。适用于提高商家信誉度,产品销售额度。装逼娱乐,用微图乐。图…...
基于Springboot的点餐平台
基于SpringbootVue的点餐平台的设计与实现 开发语言:Java数据库:MySQL技术:SpringbootMybatis工具:IDEA、Maven、Navicat 系统展示 用户登录 首页展示 菜品信息 菜品资讯 购物车 后台登录 用户管理 菜品分类管理 菜品信息管理 …...
C# 获取一个字符串中非数字部分?
方法一:使用正则表达式 使用正则表达式可以便捷地匹配并提取出字符串中所有非数字字符。与之前保留数字时的做法相反,这次我们将匹配数字并替换为空字符串,从而留下非数字部分。 using System; using System.Text.RegularExpressions;publi…...
今日总结2024/5/7
今日复习LIS二分优化的使用 P2782 友好城市 确定一边城市排序完后,另外一边满足坐标上升的最大数目即是桥的最大个数 为上升子序列模型 #include <iostream> #include <algorithm> #include <utility> #define x first #define y second cons…...
爬虫学习(3)豆瓣电影
代码 import requests import jsonif __name__ "__main__":url https://movie.douban.com/j/chart/top_list#post请求参数处理(同get请求一致)headers {"User-Agent": Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/53…...
GNU Radio创建FFT、IFFT C++ OOT块
文章目录 前言一、GNU Radio官方FFT弊端二、创建自定义的 C OOT 块1、创建 OOT 模块2、创建 OOT 块3、修改 C 和 CMAKE 文件4、编译及安装 OOT 块 三、测试1、grc 图2、运行结果①、时域波形对比②、频谱图对比 四、资源自取 前言 GNU Radio 自带的 FFT 模块使用起来不是很方便…...
125.两两交换链表中的节点(力扣)
题目描述 代码解决及思路 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), …...
APP精准推送广告是怎么做到的?
你有没有遇到这种情况,刚和家人聊起五一去哪玩,各种软件就刷到各地旅游景点。刚和朋友说到健身计划,转眼间网购平台就给你推荐各种健身用品,这些软件是如何知道我们的需求,难道我们的手机被监听了?从技术上…...
RapidJSON介绍
1.简介 RapidJSON 是一个 C 的 JSON 解析库,由腾讯开源。 支持 SAX 和 DOM 风格的 API,并且可以解析、生成和查询 JSON 数据。RapidJSON 快。它的性能可与strlen() 相比。可支持 SSE2/SSE4.2 加速。RapidJSON 独立。它不依赖于 BOOST 等外部库。它甚至…...
大型企业总分支多区域数据传输,效率为先还是安全为先?
大型企业为了业务拓展需要,会在全国乃至全球各地设立分公司和办事机构,以便更好地处理当地事务,并进行市场的开拓和客户维护,此时,企业内部就衍生出了新的业务需求,即多区域数据传输。 多区域很难准确定义&…...
C语言例题35、反向输出字符串(指针方式),例如:输入abcde,输出edcba
#include <stdio.h>void reverse(char *p) {int len 0;while (*p ! \0) { //取得字符串长度p;len;}while (len > 0) { //反向打印到终端printf("%c", *--p);len--;} }int main() {char s[255];printf("请输入一个字符串:");gets(s)…...
场景文本检测识别学习 day09(Swin Transformer论文精读)
Patch & Window 在Swin Transformer中,不同层级的窗口内部的补丁数量是固定的,补丁内部的像素数量也是固定的,如上图的红色框就是不同的窗口(Window),窗口内部的灰色框就是补丁(Patch&#…...
抖音小店个人店和个体店有什么不同?区别问题,新手必须了解!
哈喽~我是电商月月 新手开抖音小店入驻时会发现,选择入驻形式时有三个选择,个人店,个体店和企业店 其中,个人店和个体店只差了一个字,但个人店不需要营业执照,是不是入驻时选择个人店会更好一点呢&#x…...
动态规划入门和应用示例
文章目录 前言斐波那契数列爬楼梯总结优点:缺点: 前言 动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的数学方法。它主要用于解决一类具有重叠子问题和最优子结构性质的问题。…...
【C语言】精品练习题
目录 题目一: 题目二: 题目三: 题目四: 题目五: 题目六: 题目七: 题目八: 题目九: 题目十: 题目十一: 题目十二: 题目十…...
数据库(MySQL)—— DML语句
数据库(MySQL)—— DML语句 什么是DML语句添加数据给全部字段添加数据批量添加数据 修改数据删除数据 什么是DML语句 在MySQL中,DML(Data Manipulation Language,数据操纵语言)语句主要用于对数据库中的数…...
【最大公约数 并集查找 调和级数】1998. 数组的最大公因数排序
本文涉及知识点 最大公约数 并集查找 调和级数 LeetCode1998. 数组的最大公因数排序 给你一个整数数组 nums ,你可以在 nums 上执行下述操作 任意次 : 如果 gcd(nums[i], nums[j]) > 1 ,交换 nums[i] 和 nums[j] 的位置。其中 gcd(nums…...
iOS实现一个高性能的跑马灯
效果图 该跑马灯完全通过CATextLayer 实现,轻量级,并且通过 系统的位移动画实现滚动效果,避免了使用displaylink造成的性能瓶颈,使用系统动画,系统自动做了很多性能优化,实现更好的性能,并使用…...
MySQL的视图、存储过程、触发器
视图 介绍 视图是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...




