【优选算法篇】双指针的华丽探戈:深入C++算法殿堂的优雅追寻

文章目录

  • C++ 双指针详解:进阶题解与思维分析
    • 前言
    • 第一章:有效三角形的个数
      • 1.1 有效三角形的个数
        • 示例 1:
        • 示例 2:
        • 解法一(暴力求解)
        • 解法二(排序 + 双指针)
        • 易错点提示
        • 代码解读
    • 第二章:和为 s 的两个数字
      • 2.1 和为 s 的两个数字
        • 示例 1:
        • 解法一(暴力解法)
        • 解法二(双指针 - 对撞指针)
    • 第三章:三数之和与四数之和
      • 3.1 三数之和
        • 示例 1:
        • 示例 2:
        • 示例 3:
        • 提示:
        • 解法(排序+双指针)
      • 3.2 四数之和
        • 示例 1:
        • 示例 2:
        • 提示:
        • 解法(排序 + 双指针)
    • 写在最后

C++ 双指针详解:进阶题解与思维分析

💬 欢迎讨论:如有疑问或见解,欢迎在评论区留言互动。

👍 点赞、收藏与分享:如觉得这篇文章对您有帮助,请点赞、收藏并分享!
🚀 分享给更多人:欢迎分享给更多对 C++ 感兴趣的朋友,一起学习双指针的基础与进阶!


前言

接上篇【优选算法篇】双指针的优雅舞步:C++ 算法世界的浪漫探索

本篇文章将带领大家进入双指针的进阶领域。在基础篇中,我们已经学习了如何利用双指针优化简单数组问题,而在这一篇中,我们将进一步深入探讨双指针的高级应用场景,包括排序问题、多数之和等经典题型的双指针解法,以及如何利用双指针快速解决复杂的数组与链表问题。

通过更加深入的题目分析和双指针的高级策略,我们希望大家能够更加熟练地运用这一算法技巧,应对更具挑战性的编程问题。让我们继续双指针的优雅舞步,开启 C++ 算法世界的浪漫探索!


第一章:有效三角形的个数

1.1 有效三角形的个数

题目链接:611. 有效三角形的个数
题目描述:给定一个包含非负整数的数组 nums,返回其中可以组成三角形三条边的三元组个数。

示例 1:
  • 输入:nums = [2, 2, 3, 4]
  • 输出:3
  • 解释:有效的组合是:
    • 2, 3, 4 (使用第一个 2)
    • 2, 3, 4 (使用第二个 2)
    • 2, 2, 3
示例 2:
  • 输入:nums = [4, 2, 3, 4]
  • 输出:4
  • 解释:
    • 4, 2, 3
    • 4, 2, 4
    • 4, 3, 4
    • 2, 3, 4

解法一(暴力求解)

算法思路

  • 三层 for 循环枚举出所有的三元组,并判断是否能构成三角形。
  • 判断三角形的优化:
    • 如果能构成三角形,需要满足任意两边之和大于第三边。但实际上只需让较小的两条边之和大于第三边即可。
    • 因此可以先将原数组排序,然后从小到大枚举三元组,一方面省去枚举的数量,另一方面方便判断是否能构成三角形。

代码实现

class Solution {
public:int triangleNumber(vector<int>& nums) {// 1. 排序sort(nums.begin(), nums.end());int n = nums.size(), ret = 0;// 2. 从小到大枚举所有的三元组for (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {for (int k = j + 1; k < n; k++) {// 当最小的两个边之和大于第三边的时候,统计答案if (nums[i] + nums[j] > nums[k])ret++;}}}return ret;}
};

复杂度分析

  • 时间复杂度O(n^3),对于较大的输入,三层循环会导致性能问题,适合小规模数据。
  • 空间复杂度O(1),只使用了常数个变量存储结果和中间值。

解法二(排序 + 双指针)

算法思路

  • 先将数组排序。
  • 根据「解法一」中的优化思想,可以固定一个「最长边」,然后在比这条边小的有序数组中找出一个二元组,使这个二元组之和大于这个最长边。由于数组是有序的,我们可以利用「对撞指针」来优化。
  • 设最长边枚举到位置 i,区间 [left, right]i 位置左边的区间(也就是比它小的区间):
    • 如果 nums[left] + nums[right] > nums[i]
      • 说明 [left, right - 1] 区间上的所有元素均可以与 nums[right] 构成比 nums[i] 大的二元组。
      • 满足条件的有 right - left 种。
      • 此时 right 位置的元素的所有情况相当于全部考虑完毕,right--,进入下一轮判断。
    • 如果 nums[left] + nums[right] <= nums[i]
      • 说明 left 位置的元素不可能与 [left + 1, right] 位置上的元素构成满足条件的二元组。
      • left 位置的元素可以舍去,left++ 进入下一轮循环。

代码实现

class Solution {
public:int triangleNumber(vector<int>& nums) {// 1. 排序sort(nums.begin(), nums.end());// 2. 利用双指针解决问题int ret = 0, n = nums.size();for (int i = n - 1; i >= 2; i--) { // 先固定最大的数// 利用双指针快速统计符合要求的三元组的个数int left = 0, right = i - 1;while (left < right) {if (nums[left] + nums[right] > nums[i]) {ret += right - left;right--;} else {left++;}}}return ret;}
};

复杂度分析

  • 时间复杂度O(n^2),排序的时间复杂度为 O(n log n),之后每个元素使用双指针进行一次遍历,时间复杂度为 O(n^2)
  • 空间复杂度O(1),只使用了常数个变量存储结果和指针位置。

易错点提示
  1. 指针移动逻辑:在双指针遍历时,根据条件选择移动 leftright,确保找到所有满足条件的三元组。
  2. 数组排序:在开始双指针遍历之前,必须对数组进行排序,否则无法保证正确性。
  3. 三角形判定条件:确保只需判断两边之和是否大于第三边,简化条件判断,避免遗漏有效三元组。

代码解读

该算法的时间复杂度为 O(n^2),空间复杂度为 O(1),适合大规模输入。通过排序和双指针优化,能够有效减少暴力求解中的不必要计算,提升性能。此方法非常适合在数组问题中应用,能够快速找到所有满足条件的组合。


第二章:和为 s 的两个数字

2.1 和为 s 的两个数字

题目链接:剑指 Offer 57. 和为s的两个数字
题目描述:输入一个递增排序的数组和一个数字 s,在数组中查找两个数,使得它们的和正好是 s。如果有多对数字的和等于 s,则输出任意一对即可。

示例 1:
  • 输入:nums = [2, 7, 11, 15], target = 9
  • 输出:[2, 7] 或者 [7, 2]

解法一(暴力解法)

算法思路

  • 两层 for 循环列出所有两个数字的组合,判断是否等于目标值。

算法流程

  • 两层 for 循环:
    • 外层 for 循环依次枚举第一个数 a
    • 内层 for 循环依次枚举第二个数 b,让它与 a 匹配;
    • 然后将挑选的两个数相加,判断是否符合目标值。

代码实现

class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {int n = nums.size();for (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {if (nums[i] + nums[j] == target)return {nums[i], nums[j]};}}return {-1, -1};}
};

解法二(双指针 - 对撞指针)

算法思路

  • 注意到本题是升序的数组,因此可以使用「对撞指针」优化时间复杂度。

算法流程

  • 初始化 leftright 分别指向数组的左右两端:
    • left < right 的时候,一直循环:
      • nums[left] + nums[right] == target 时,说明找到结果,记录结果,并返回;
      • nums[left] + nums[right] < target 时:
        • 说明当前和小于目标值,需要增大和,left++
      • nums[left] + nums[right] > target 时:
        • 说明当前和大于目标值,需要减小和,right--

代码实现

class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while (left < right) {int sum = nums[left] + nums[right];if (sum > target) right--;else if (sum < target) left++;else return {nums[left], nums[right]};}//这是为了照顾编译器,不然编译器报错:不是所有的路径都有返回值return {-1, -1};}
};

第三章:三数之和与四数之和

3.1 三数之和

题目链接:15. 三数之和
题目描述:给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k,同时还满足 nums[i] + nums[j] + nums[k] == 0。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:
  • 输入:nums = [-1, 0, 1, 2, -1, -4]
  • 输出:[[-1, -1, 2], [-1, 0, 1]]
  • 解释:
    • nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0
    • nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0
    • nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0
    • 不同的三元组是 [-1, 0, 1][-1, -1, 2]
    • 注意,输出的顺序和三元组的顺序并不重要。
示例 2:
  • 输入:nums = [0, 1, 1]
  • 输出:[]
  • 解释:唯一可能的三元组和不为 0。
示例 3:
  • 输入:nums = [0, 0, 0]
  • 输出:[[0, 0, 0]]
  • 解释:唯一可能的三元组和为 0。
提示:
  • 3 <= nums.length <= 3000
  • -10^5 <= nums[i] <= 10^5

解法(排序+双指针)

算法思路

  • 本题与两数之和类似,是非常经典的面试题。
  • 与两数之和稍微不同的是,题目中要求找到所有「不重复」的三元组。我们可以利用双指针思想来对暴力枚举做优化:
    1. 先排序;
    2. 然后固定一个数 a
    3. 在这个数后面的区间内,使用「双指针算法」快速找到两个数之和等于 -a 即可。
  • 需要注意的是,这道题中需要有「去重」操作:
    1. 找到一个结果之后,leftright 指针要「跳过重复」的元素;
    2. 当使用完一次双指针算法之后,固定的 a 也要「跳过重复」的元素。

代码实现

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> ret;// 1. 排序sort(nums.begin(), nums.end());// 2. 利用双指针解决问题int n = nums.size();for (int i = 0; i < n; ) { // 固定数 aif (nums[i] > 0) break; // 小优化int left = i + 1, right = n - 1, target = -nums[i];while (left < right) {int sum = nums[left] + nums[right];if (sum > target) right--;else if (sum < target) left++;else {ret.push_back({nums[i], nums[left], nums[right]});left++, right--;// 去重操作 left 和 rightwhile (left < right && nums[left] == nums[left - 1]) left++;while (left < right && nums[right] == nums[right + 1]) right--;}}// 去重 ii++;while (i < n && nums[i] == nums[i - 1]) i++;}return ret;}
};

3.2 四数之和

题目链接:18. 四数之和
题目描述:给你一个由 n 个整数组成的数组 nums,和一个目标值 target。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]](若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按任意顺序返回答案。

示例 1:
  • 输入:nums = [1, 0, -1, 0, -2, 2], target = 0
  • 输出:[[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]
示例 2:
  • 输入:nums = [2, 2, 2, 2, 2], target = 8
  • 输出:[[2, 2, 2, 2]]
提示:
  • 1 <= nums.length <= 200
  • -10^9 <= nums[i] <= 10^9
  • -10^9 <= target <= 10^9

解法(排序 + 双指针)

算法思路

  1. 依次固定一个数 a
  2. 在这个数 a 的后面区间上,利用「三数之和」找到三个数,使这三个数的和等于 target - a 即可。

代码实现

class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;// 1. 排序sort(nums.begin(), nums.end());// 2. 利用双指针解决问题int n = nums.size();for (int i = 0; i < n; ) { // 固定数 afor (int j = i + 1; j < n; ) { // 固定数 b// 双指针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[i], nums[j], nums[left++], nums[right--]});// 去重操作 left 和 rightwhile (left < right && nums[left] == nums[left - 1]) left++;while (left < right && nums[right] == nums[right + 1]) right--;}}// 去重 jj++;while (j < n && nums[j] == nums[j - 1]) j++;}// 去重 ii++;while (i < n && nums[i] == nums[i - 1]) i++;}return ret;}
};

写在最后

在本篇文章中,我们沿着双指针的足迹,走进了更为复杂的算法世界。从基础的排序与两数之和,到多元问题的优化解法,双指针以其灵活而高效的策略,为我们提供了简洁优雅的解题思路。无论是解锁数组中的隐藏结构,还是精确处理链表中的循环,双指针始终如同算法中的舞者,轻巧地穿梭于问题的复杂性之间,帮助我们化繁为简。

希望通过这些进阶题解,大家能不仅熟悉双指针的运用技巧,更能深刻体会到算法设计中的思维之美。未来的算法旅程中,无论面对怎样的挑战,双指针这一工具都能在你的编程工具箱中,成为应对复杂问题时得心应手的利器。

以上就是关于【优选算法篇】双指针的华丽探戈:深入C++算法殿堂的优雅追寻的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️

在这里插入图片描述

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

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

相关文章

在线文字转语音哪个好?教你7个ai文字转语音配音的方法,别错过!

随着人工智能技术的飞速发展&#xff0c;ai文字转语音配音软件已经开始广泛应用&#xff0c;能够生成或修改出逼真的人声。这些文字转语音在线版不仅易于使用&#xff0c;连没有专业音频处理技术的用户也能轻松上手。通过这些软件&#xff0c;您只需简单输入文本或上传音频文件…

【随时随地学算法】本地部署hello-algo结合内网穿透远程学习新体验

文章目录 前言1.关于hello-algo2.安装Docker和Docker compose3.本地部署hello-algo4. hello-algo本地访问5.cpolar内网穿透工具安装6.创建远程连接公网地址7.固定Uptime Kuma公网地址 前言 本篇文章主要介绍如何在本地部署hello-algo算法学习必备项目&#xff0c;并结合cpolar…

PostgreSQL 17重磅登场——世界上最成功的数据库

朋友们&#xff0c;万众期待的 PostgreSQL 大版本发布又来了&#xff01;这一次&#xff0c;PostgreSQL 17 带着全新的性能优化和开发者必备的新功能强势登场。与其说这是一场普通的更新&#xff0c;不如说它是一场专为高并发工作负载和海量数据量身打造的技术嘉年华&#xff0…

【优选算法篇】编织算法的流动诗篇:滑动窗口的轻盈之美

文章目录 C 滑动窗口详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;热身练习1.1 长度最小的子数组解法一&#xff08;暴力求解&#xff09;解法二&#xff08;滑动窗口&#xff09;滑动窗口的核心思想图解分析滑动窗口的有效性时间复杂度分析易错点提示 1.2 无重复…

基于webrtc实现音视频通信

与传统通信方式不同&#xff0c;p2p通信的实现过程不依赖于中间服务器的信息收发&#xff0c;直接通过信令等完成通信过程的建立&#xff1b; 通过websocket实现信令服务器的建立&#xff0c;而通过信令来确定通信双方&#xff1b; webrtc通过 sdp协议来完善通信双方间协议的…

塞班和诺基亚(中古手机图,你见过哪个?)

诺基亚的塞班系统&#xff0c;是比较早和强大的移动操作系统了。当时还有Palm&#xff0c;微软的平台&#xff0c;但市占率都很低。 安卓从被谷歌收购那天&#xff0c;每个特性都预示着&#xff0c;未来一定会超越塞班。而塞班后来取消了生态&#xff0c;自己来使用&#xff0c…

mac上docker desktop 服务指南

容器化技术是指将软件代码与运行此代码所需的操作系统 (OS) 库和依赖项进行集体打包&#xff0c;以便创建可在任意基础设施上一致运行的单个轻量级可执行文件&#xff08;称为容器&#xff09;&#xff0c;比物理机部署具备更好的可移植性和维护性&#xff0c;比虚拟机具有更高…

基于Spring Boot+Vue的医疗健康的便民服务平台系统的设计与实现(协同过滤算法、实时聊天)

&#x1f388;系统亮点&#xff1a;协同过滤算法、实时聊天&#xff1b; 一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17 前端&#xff1a; 技术&#xff1a;框架…

atcoder abc375

A seats 代码&#xff1a; #include <bits/stdc.h> using namespace std;int main() {int n;cin >> n;vector<char> a(n 1);for(int i 1; i < n; i ) cin >> a[i];int cnt 0;for(int i 1; i < n - 2; i ) {if(a[i] # && a[i 1…

【spring ai】java 实现RAG检索增强,超快速入门

rag 需求产生的背景介绍&#xff1a; 在使用大模型时&#xff0c;一个常见的问题是模型会产生幻觉&#xff08;即生成的内容与事实不符&#xff09;&#xff0c;同时由于缺乏企业内部数据的支持&#xff0c;导致其回答往往不够精准和具体&#xff0c;偏向于泛泛而谈。这些问题…

STM32 实现 TCP 服务器与多个设备通信

目录 一、引言 二、硬件准备 三、软件准备 四、LWIP 协议栈的配置与初始化 五、创建 TCP 服务器 1.创建 TCP 控制块 2.绑定端口 3. 进入监听状态 4.设置接收回调函数 六、处理多个客户端连接 七、数据处理与通信管理 八、错误处理与资源管理 九、总结 一、引…

【C++】:工厂模式

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 简单工厂模什么是简单工厂模式&#xff1f;如何实现简单工厂模式&#xff1f; 工厂方法抽象工厂模式总结简单工厂模式工厂方法抽象工厂「Abstract Factory」 简单工厂模 什么是简单工厂模式&#xf…

ps提示不能使用移动工具,因为目标通道被隐藏的解决办法

解决&#xff1a;按F7&#xff0c;或者从窗口把图层打开 按图示找到快速蒙版图层。它可能被隐藏或以特殊图标显示。右键删除或者拖到右下角垃圾桶里

小猿口算炸鱼脚本

目录 写在前面&#xff1a; 一、关于小猿口算&#xff1a; 二、代码逻辑 1.数字识别 2.答题部分 三、代码分享&#xff1a; 补充&#xff1a;软件包下载 写在前面&#xff1a; 最近小猿口算已经被不少大学生攻占&#xff0c;小学生直呼有挂。原本是以为大学生都打着本…

从融资烧钱到商业落地:中国AI大模型步入「实战期」

在AI还尚且未达到生产力工具的时候&#xff0c;没人能知道怎样的基础模型会是尽头&#xff0c;以及对付费客户而言&#xff0c;他们如何才能将这笔投入转化为真实营收。 而对于大模型究竟什么能盈利&#xff0c;目前国内的任何一家都未表过态。或者说&#xff0c;这不是一个当…

从0开始深度学习(11)——多层感知机

前面介绍了线性神经网络&#xff0c;但是线性模型是有可能出错的&#xff0c;因为线性模型意味着是单调假设&#xff0c;但是现实中往往很复杂。例如&#xff0c;我们想要根据体温预测死亡率。 对体温高于37摄氏度的人来说&#xff0c;温度越高风险越大。 然而&#xff0c;对体…

MySQL插入优化-性能对比

插入优化主要包括&#xff1a; 批量插入条数据&#xff0c;而不是单个记录逐条插入。手动提交事务&#xff0c;避免自动提交事务带来的额外开销。使用load命令从本地文件导入。 性能对比 创建数据库表 CREATE TABLE if not exists tb_sku ( id int(20) …

facefusion,使用CPU实现一键图片、视频换脸,无需显卡,无限时长(附下载即用的整合包)

FaceFusion 是一种利用人工智能技术将两张或多张人脸融合在一起的图像处理技术。这种技术通过面部特征的识别和重构&#xff0c;将不同的人脸混合成一张新的脸&#xff0c;生成的图像看起来像是这些人脸的融合体。 FaceFusion使用深度学习算法&#xff0c;来捕捉人脸的细节和特…

OpenCV答题卡识别

文章目录 一、基本流程二、代码实现1.定义函数2.图像预处理&#xff08;1&#xff09;高斯模糊、边缘检测&#xff08;2&#xff09;轮廓检测&#xff08;3&#xff09;透视变换&#xff08;4&#xff09;阈值处理和轮廓检测 3.筛选和排序选项轮廓4.判断答案5.显示结果 三、总结…

YOLOv11改进有效系列目录 - 包含卷积、主干、检测头、注意力机制、Neck上百种创新机制 - 针对多尺度、小目标、遮挡、恶劣天气等问题

目标检测作为计算机视觉领域的一项核心任务&#xff0c;极大地推动了整个领域的发展。它不仅是其他许多视觉任务的基础工具&#xff0c;还在学术研究和实际应用之间架起了一座桥梁。目标检测的主要任务是识别和定位图像或视频中的特定对象&#xff0c;通常需要模型能同时处理多…