当前位置: 首页 > news >正文

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

Alt

🔥个人主页Quitecoder

🔥专栏算法笔记仓

Alt

朋友们大家好啊,本篇文章我们来到算法的双指针的第二部分

目录

  • `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

步骤如下:

  1. 对数组 nums 进行升序排序
  2. 初始化计数器 count 为 0
  3. 从后往前遍历数组(从最大值开始,下标为 i),我们将这个值作为潜在的最长边 c
  4. 对于每一个 c,设置两个指针:pre 指针指向数组的开始(下标为 0),lat 指针指向 c 之前的元素(下标为 i - 1
  5. pre 指针小于 lat 指针时:
    • 计算 nums[pre]nums[lat] 的和,将这个和与 nums[i](也就是当前的 c)进行比较
    • 如果 nums[pre] + nums[lat] > nums[i],由于数组已经排序,所有在 prelat 之间的元素与 nums[lat] 的和都会大于 nums[i],所以我们可以将 lat - pre 个三角形加到 count
    • 然后将 lat 向左移动一位(减小一点以寻找下一个可能的三角形)
    • 如果和小于等于 nums[i],我们将 pre 向右移动一位(增大一点以寻找可能的三角形)
  6. 当处理完所有的 c 后,返回 count 作为结果

本道题还是很简单的

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

题目链接:LCR 179.查找总价格为目标值的两个商品
题目描述在这里插入图片描述

算法的具体思路:

  1. 初始化两个指针,pre 指向数组的开始(索引 0),last 指向数组的末尾(索引 price.size() - 1
vector<int> s1;
int last=price.size()-1;
int pre=0;
  1. 进行一个 while 循环,在数组两端移动 prelast 指针直到它们相遇。循环的条件是 pre < last,确保没有重复使用相同的元素。

  2. 在每次循环中,计算两个指针指向的数的和,判断这个和与目标值 target 的关系:

    • 如果和大于 target,那么为了减小和,last 指针左移(减小索引值)
    • 如果和小于 target,那么为了增大和,pre 指针右移(增加索引值)
    • 如果和等于 target
      • 将这两个数添加到结果 vector s1 中。
      • 因为只需要一组解,所以找到一对满足条件的数之后,通过 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;}
}
  1. 返回结果 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;}
};

注意的要点:

  1. 唯一性:返回的结果中不能包含重复的三元组。解决方法是在找到一个符合条件的组合后,跳过所有相同的元素

  2. 遍历策略:外层循环遍历数组,内层使用双指针从两端向中间查找两个其他元素,以保证三个数的和为零

  3. 跳过重复元素

  • 在外层循环中,如果当前的数字与前一个数字相同,则跳过以避免重复的三元组
 for(int i=0;i<nums.size()-2;i++)
{if(i>0&&nums[i-1]==nums[i])continue;
  • 在找到一个满足条件的三元组之后,同时跳过 pre 指针的连续重复数字,并将 pre 指针向右移动
  • 同样地,跳过 las 指针的连续重复数字,并将 las 指针向左移动
  1. 寻找条件:三数之和等于零。这意味着在内层循环中,如果 nums[pre] + nums[las] 小于 -nums[i],则需要右移 pre 指针;如果大于 -nums[i],则需要左移 las 指针;如果等于 -nums[i],则记录该三元组,继续寻找其他可能的组合

  2. 边界条件

    • 外层循环的循环变量 i 应小于 nums.size() - 2,因为需要至少3个数来组成一个三元组
    • prelas 指针相遇时,内层循环结束

我们还可以进一步优化,当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.双指针常见场景总结

双指针主要应用在有序数组或链表的问题中,以及一些可以通过前后关系来优化问题的场景:

  1. 有序数组的对撞指针

    • 两数之和:在有序数组中找到两个数,使它们的和为特定的目标值
    • 三数之和/四数之和:与两数之和类似,但需要找到三个或四个数的组合
    • 移除元素:从有序数组中移除重复项或特定值,并返回新数组的长度
  2. 快慢指针

    • 链表中环的检测:使用快慢指针检测链表是否有环,快指针一次移动两步,慢指针一次移动一步
    • 寻找链表中点:使用快慢指针找到链表的中间节点,快指针结束时慢指针在中点
    • 寻找链表的倒数第k个元素:快指针先移动k步,然后快慢指针共同移动,快指针到达末尾时慢指针所在位置即倒数第k个元素
  3. 前后指针

    • 归并排序中的合并步骤:使用两个指针分别指向两个有序数组的开始位置,以合并成一个新的有序数组。
    • 对链表进行操作:在链表上进行操作时,如删除节点或反转链表,常常需要前后指针来保持结点的连接。
  4. 左右指针

    • 二分查找:在有序数组中查找元素,使用左右指针限定查找范围

双指针方法的关键在于,指针的移动可以依据问题的规律来减少不必要的比较或计算,从而提高算法效率。当然,双指针的使用需要充分理解问题的性质,并巧妙设计指针的移动策略。在很多问题中,双指针技术都能将时间复杂度从 O(n2) 优化到 O(n),超级好用

本节内容到此结束!!感谢大家阅读!!

相关文章:

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

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;算法笔记仓 朋友们大家好啊&#xff0c;本篇文章我们来到算法的双指针的第二部分 目录 1.有效三角形的个数2.查找总价格为目标值的两个商品3.三数之和4.四数之和5.双指针常见场景总结 1.有效三角形…...

微图乐 多种装B截图一键制作工具(仅供娱乐交流)

软件介绍 采用exe进程交互通信。全新UI界面&#xff0c;让界面更加清爽简约。支持zfb、VX、TX、Yin行、Dai款、游戏等图片生成&#xff0c;一键超清原图复制到剪辑板&#xff0c;分享给好友。适用于提高商家信誉度&#xff0c;产品销售额度。装逼娱乐&#xff0c;用微图乐。图…...

基于Springboot的点餐平台

基于SpringbootVue的点餐平台的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页展示 菜品信息 菜品资讯 购物车 后台登录 用户管理 菜品分类管理 菜品信息管理 …...

C# 获取一个字符串中非数字部分?

方法一&#xff1a;使用正则表达式 使用正则表达式可以便捷地匹配并提取出字符串中所有非数字字符。与之前保留数字时的做法相反&#xff0c;这次我们将匹配数字并替换为空字符串&#xff0c;从而留下非数字部分。 using System; using System.Text.RegularExpressions;publi…...

今日总结2024/5/7

今日复习LIS二分优化的使用 P2782 友好城市 确定一边城市排序完后&#xff0c;另外一边满足坐标上升的最大数目即是桥的最大个数 为上升子序列模型 #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请求参数处理&#xff08;同get请求一致&#xff09;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精准推送广告是怎么做到的?

你有没有遇到这种情况&#xff0c;刚和家人聊起五一去哪玩&#xff0c;各种软件就刷到各地旅游景点。刚和朋友说到健身计划&#xff0c;转眼间网购平台就给你推荐各种健身用品&#xff0c;这些软件是如何知道我们的需求&#xff0c;难道我们的手机被监听了&#xff1f;从技术上…...

RapidJSON介绍

1.简介 RapidJSON 是一个 C 的 JSON 解析库&#xff0c;由腾讯开源。 支持 SAX 和 DOM 风格的 API&#xff0c;并且可以解析、生成和查询 JSON 数据。RapidJSON 快。它的性能可与strlen() 相比。可支持 SSE2/SSE4.2 加速。RapidJSON 独立。它不依赖于 BOOST 等外部库。它甚至…...

大型企业总分支多区域数据传输,效率为先还是安全为先?

大型企业为了业务拓展需要&#xff0c;会在全国乃至全球各地设立分公司和办事机构&#xff0c;以便更好地处理当地事务&#xff0c;并进行市场的开拓和客户维护&#xff0c;此时&#xff0c;企业内部就衍生出了新的业务需求&#xff0c;即多区域数据传输。 多区域很难准确定义&…...

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("请输入一个字符串&#xff1a;");gets(s)…...

场景文本检测识别学习 day09(Swin Transformer论文精读)

Patch & Window 在Swin Transformer中&#xff0c;不同层级的窗口内部的补丁数量是固定的&#xff0c;补丁内部的像素数量也是固定的&#xff0c;如上图的红色框就是不同的窗口&#xff08;Window&#xff09;&#xff0c;窗口内部的灰色框就是补丁&#xff08;Patch&#…...

抖音小店个人店和个体店有什么不同?区别问题,新手必须了解!

哈喽~我是电商月月 新手开抖音小店入驻时会发现&#xff0c;选择入驻形式时有三个选择&#xff0c;个人店&#xff0c;个体店和企业店 其中&#xff0c;个人店和个体店只差了一个字&#xff0c;但个人店不需要营业执照&#xff0c;是不是入驻时选择个人店会更好一点呢&#x…...

动态规划入门和应用示例

文章目录 前言斐波那契数列爬楼梯总结优点&#xff1a;缺点&#xff1a; 前言 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是运筹学的一个分支&#xff0c;是求解决策过程最优化的数学方法。它主要用于解决一类具有重叠子问题和最优子结构性质的问题。…...

【C语言】精品练习题

目录 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目八&#xff1a; 题目九&#xff1a; 题目十&#xff1a; 题目十一&#xff1a; 题目十二&#xff1a; 题目十…...

数据库(MySQL)—— DML语句

数据库&#xff08;MySQL&#xff09;—— DML语句 什么是DML语句添加数据给全部字段添加数据批量添加数据 修改数据删除数据 什么是DML语句 在MySQL中&#xff0c;DML&#xff08;Data Manipulation Language&#xff0c;数据操纵语言&#xff09;语句主要用于对数据库中的数…...

【最大公约数 并集查找 调和级数】1998. 数组的最大公因数排序

本文涉及知识点 最大公约数 并集查找 调和级数 LeetCode1998. 数组的最大公因数排序 给你一个整数数组 nums &#xff0c;你可以在 nums 上执行下述操作 任意次 &#xff1a; 如果 gcd(nums[i], nums[j]) > 1 &#xff0c;交换 nums[i] 和 nums[j] 的位置。其中 gcd(nums…...

iOS实现一个高性能的跑马灯

效果图 该跑马灯完全通过CATextLayer 实现&#xff0c;轻量级&#xff0c;并且通过 系统的位移动画实现滚动效果&#xff0c;避免了使用displaylink造成的性能瓶颈&#xff0c;使用系统动画&#xff0c;系统自动做了很多性能优化&#xff0c;实现更好的性能&#xff0c;并使用…...

MySQL的视图、存储过程、触发器

视图 介绍 视图是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&#xff0c;不保存查询结果。所以我们在创建视图的时…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...