双指针技巧在C++中的应用:从基础到进阶

目录

1.简介

2.同向双指针

2.1.数组去重

2.2.最大子数组和

2.3.链表反转

2.4.字符串匹配(简单版)

3.对向双指针

3.1.两数之和(有序数组)

3.2.盛最多水的容器

4.快慢指针

4.1.判断链表是否有环

4.2.寻找链表的中间节点

4.3.合并两个有序链表

5.总结


1.简介

        双指针技巧是一种常见的算法技巧,广泛应用于排序、查找、求和等问题中,尤其在处理数组、链表等数据结构时,表现出显著的优势。通过合理地使用两个指针来解决问题,可以减少时间复杂度,提升算法效率。

        双指针技巧在 C++ 中应用广泛,能高效解决诸多算法问题,主要分为同向双指针、对向双指针和快慢双指针这几类。

        以下结合具体应用案例来介绍。

2.同向双指针

2.1.数组去重

        给定一个有序数组,要求去除重复元素并返回新数组的长度。以[1, 1, 2, 2, 3, 4]为例,借助同向双指针,慢指针slow用于记录不重复元素的存储位置,快指针fast遍历数组。当fast指向的元素与slow指向的元素不同时,将fast指向的元素赋值给slow + 1的位置,然后slow后移。

        代码如下:

int removeDuplicates(vector<int>& nums) {if (nums.empty()) return 0;int slow = 0;for (int fast = 1; fast < nums.size(); ++fast) {if (nums[fast] != nums[slow]) {nums[++slow] = nums[fast];}}return slow + 1;
}

2.2.最大子数组和

给定一个整数数组,找出具有最大和的连续子数组(子数组至少包含一个元素)。

思路

  1. 使用一个指针来表示当前的窗口区间。

  2. 每次扩展窗口,计算窗口内的元素和,并更新最大和。

  3. 一旦当前窗口的和小于0,可以通过左指针缩小窗口,减少不必要的计算。

#include <iostream>
#include <vector>
usingnamespacestd;int maxSubArray(const vector<int>& nums) {int max_sum = nums[0], current_sum = nums[0];for (int i = 1; i < nums.size(); i++) {current_sum = max(nums[i], current_sum + nums[i]);max_sum = max(max_sum, current_sum);}return max_sum;
}int main() {vector<int> nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};cout << "Maximum Subarray Sum: " << maxSubArray(nums) << endl;return 0;
}

2.3.链表反转

反转链表的经典问题,可以通过双指针技巧进行高效处理。

思路

  1. 使用两个指针,一个指向当前节点,另一个指向前一个节点。

  2. 每次将当前节点的指针指向前一个节点,逐步反转链表。

#include <iostream>
usingnamespacestd;struct ListNode {int val;ListNode* next;ListNode(int x) : val(x), next(nullptr) {}
};ListNode* reverseList(ListNode* head) {ListNode* prev = nullptr;ListNode* current = head;while (current != nullptr) {ListNode* nextNode = current->next;current->next = prev;prev = current;current = nextNode;}return prev;
}void printList(ListNode* head) {ListNode* temp = head;while (temp != nullptr) {cout << temp->val << " ";temp = temp->next;}cout << endl;
}int main() {ListNode* head = new ListNode(1);head->next = new ListNode(2);head->next->next = new ListNode(3);head->next->next->next = new ListNode(4);cout << "Original List: ";printList(head);ListNode* reversed = reverseList(head);cout << "Reversed List: ";printList(reversed);return 0;
}

2.4.字符串匹配(简单版)

        在一个长字符串中查找短字符串首次出现的位置(简单的暴力匹配改进)。比如在字符串"ABABDABACDABABCABAB"中找"ABABC"。长字符串指针i和短字符串指针j同向移动,当j指向的字符与i指向的字符匹配时,ij都后移;若不匹配,i回退到上次匹配起始位置的下一个位置,j归零重新匹配。

        具体代码如下:

int strStr(string haystack, string needle) {int m = haystack.size(), n = needle.size();for (int i = 0; i <= m - n; ++i) {int j = 0;for (; j < n; ++j) {if (haystack[i + j] != needle[j]) {break;}}if (j == n) {return i;}}return -1;
}

3.对向双指针

3.1.两数之和(有序数组)

假设有一个排序好的数组,我们需要在该数组中找到两个数,使得它们的和等于目标值。

思路

  1. 定义两个指针,分别指向数组的开头和结尾。

  2. 根据当前两指针指向的数值之和与目标值的关系,决定移动哪个指针。

  3. 如果两数之和大于目标值,则移动右指针,减小和;如果小于目标值,则移动左指针,增大和。

        具体代码如下:

#include <iostream>
#include <vector>
usingnamespacestd;bool twoSum(const vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while (left < right) {int sum = nums[left] + nums[right];if (sum == target) {return true;} elseif (sum < target) {left++;} else {right--;}}return false;
}int main() {vector<int> nums = {1, 2, 3, 4, 6};int target = 10;cout << (twoSum(nums, target) ? "Found" : "Not found") << endl;return 0;
}

3.2.盛最多水的容器

        给定一个数组,数组中的每个元素表示一个垂直的线段高度,线段之间的距离是相邻元素的索引差,要求找出两条线段,使得它们与 x 轴构成的容器能容纳最多的水。以[1, 8, 6, 2, 5, 4, 8, 3, 7]为例,使用对向双指针,指针leftright分别指向数组两端。计算当前容器的面积area = min(height[left], height[right]) * (right - left),更新最大面积。然后比较height[left]height[right],较小值对应的指针向内移动,重复计算面积和移动指针的操作。

        具体代码如下:

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

4.快慢指针

        快慢指针的基本思路是:用两个指针(通常称为快指针和慢指针)遍历数据结构。慢指针每次移动一步,而快指针每次移动两步。由于快指针移动的速度较快,它可以在一些特定场景下帮助我们高效地解决问题。

4.1.判断链表是否有环

        环形链表是一个常见的数据结构问题,要求检测链表中是否存在环。使用快慢指针的算法非常高效。基本思路是:让快指针每次走两步,慢指针每次走一步。如果链表中存在环,快慢指针最终会相遇;如果链表没有环,快指针会先到达链表的尾部。

        具体实现代码如下:

#include <iostream>struct ListNode {int val;ListNode* next;ListNode(int x) : val(x), next(nullptr) {}
};bool hasCycle(ListNode* head) {if (!head || !head->next) returnfalse;ListNode* slow = head;ListNode* fast = head;while (fast && fast->next) {slow = slow->next;           // 慢指针每次走一步fast = fast->next->next;     // 快指针每次走两步if (slow == fast) return true; // 快慢指针相遇,说明有环}return false; // 快指针到达链表尾部,没有环
}int main() {ListNode* head = new ListNode(1);head->next = new ListNode(2);head->next->next = new ListNode(3);head->next->next->next = new ListNode(4);head->next->next->next->next = head->next; // 创建环if (hasCycle(head)) {std::cout << "The linked list has a cycle." << std::endl;} else {std::cout << "The linked list does not have a cycle." << std::endl;}return0;
}

4.2.寻找链表的中间节点

        另一个常见的应用是查找链表的中间节点。使用快慢指针时,慢指针每次走一步,快指针每次走两步。当快指针走到链表末尾时,慢指针恰好到达中间节点。

        具体实现代码如下:

#include <iostream>struct ListNode {int val;ListNode* next;ListNode(int x) : val(x), next(nullptr) {}
};ListNode* findMiddle(ListNode* head) {if (!head) returnnullptr;ListNode* slow = head;ListNode* fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;}return slow; // 慢指针指向链表的中间节点
}int main() {ListNode* head = new ListNode(1);head->next = new ListNode(2);head->next->next = new ListNode(3);head->next->next->next = new ListNode(4);head->next->next->next->next = new ListNode(5);ListNode* middle = findMiddle(head);if (middle) {std::cout << "The middle node value is: " << middle->val << std::endl;} else {std::cout << "The list is empty." << std::endl;}return0;
}

4.3.合并两个有序链表

        在合并两个有序链表时,可以使用双指针来实现。虽然这不是严格的快慢指针技巧,但它与快慢指针有一定的相似性。通过两个指针分别遍历两个链表并比较元素,逐步合并链表。

        具体实现代码如下:

#include <iostream>struct ListNode {int val;ListNode* next;ListNode(int x) : val(x), next(nullptr) {}
};ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {ListNode dummy(0);ListNode* current = &dummy;while (l1 && l2) {if (l1->val < l2->val) {current->next = l1;l1 = l1->next;} else {current->next = l2;l2 = l2->next;}current = current->next;}if (l1) current->next = l1;if (l2) current->next = l2;return dummy.next;
}int main() {ListNode* l1 = new ListNode(1);l1->next = new ListNode(3);l1->next->next = new ListNode(5);ListNode* l2 = new ListNode(2);l2->next = new ListNode(4);l2->next->next = new ListNode(6);ListNode* mergedList = mergeTwoLists(l1, l2);while (mergedList) {std::cout << mergedList->val << " ";mergedList = mergedList->next;}std::cout << std::endl;return0;
}

5.总结

        在算法题中,双指针具有很多应用,那么在实际项目中,你有使用过双指针技巧吗?主要是什么场景?欢迎评论区交流讨论~

推荐阅读

滑动窗口算法详解:概念、应用与实例,-CSDN博客

C++合并两个有序数组-CSDN博客 

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

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

相关文章

语言解码双生花:人类经验与AI算法的镜像之旅

大家好&#xff0c;我是吾鳴。 今天吾鳴要给大家分享一份由浙江大学出品的DeepSeek报告&#xff0c;报告从语言的奥秘&#xff0c;人类是如何通过语言来解码世界&#xff0c;AI又是如何理解人类的语言&#xff0c;同时介绍了当下爆火的DeepSeek-V3和DeepSeek-R1两种大模型的进化…

如何避免测试数据准备不充分或不可复用

避免测试数据准备不充分或不可复用的关键方法包括明确数据需求、统一数据管理工具、建立数据复用机制、定期维护更新测试数据以及加强团队沟通与协作。 其中&#xff0c;统一数据管理工具对确保数据质量和复用性尤为重要。例如&#xff0c;许多团队采用专门的测试数据管理工具以…

HTTP 核心知识点整理

1. HTTP 基础 ​定义&#xff1a;HTTP&#xff08;HyperText Transfer Protocol&#xff09;是应用层协议&#xff0c;基于 ​请求-响应模型&#xff0c;用于客户端&#xff08;浏览器&#xff09;与服务器之间的通信。​特点&#xff1a; ​无状态&#xff1a;每次请求独立&a…

汤臣倍健业绩倒车:2024年利润下滑超六成,三大核心品牌销量失守

撰稿|行星 来源|贝多财经 汤臣倍健的2024年&#xff0c;“隐痛”不少。 3月22日&#xff0c;国内膳食营养补充剂供应商汤臣倍健股份有限公司&#xff08;SZ:300416&#xff0c;下称“汤臣倍健”&#xff09;公布了2024年年度报告。财报显示&#xff0c;汤臣倍健过去一年出现了…

C#中的Lambda表达式‌

在C#中&#xff0c;‌Lambda表达式‌是一种比匿名方法更简洁、更灵活的语法形式&#xff0c;用于定义匿名函数&#xff08;Anonymous Function&#xff09;。它通过>运算符实现&#xff0c;能够大幅简化委托和表达式树的编写&#xff0c;是现代C#编程中广泛使用的核心特性之…

通信系统的性能指标

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、通信系统的性能指标概述二、数字通信系统的有效性指标三、数字通信系统的可靠性指标总结 前言 一、通信系统的性能指标概述 其中一个提高&#xff0c;另一个…

Linux:(模拟HTTP协议,GET和POST方法,Http的状态码)

目录 一、认识HTTP协议 1.上网的本质 2.应用层的运行逻辑 3.HTTP的概念 二、url 1.认识网址 三、HTTP协议的宏观理解 1.HTTP请求 2.HTTP响应 3.实际的HTTP请求 &#xff08;1&#xff09;测试代码 &#xff08;2&#xff09;接收HTTP请求 &#xff08;3&#xff09…

动态规划之完全背包

引言&#xff1a; 完全背包 隶属于动态规划中的背包问题。而 01背包 又是完全背包的基石&#xff0c;所以不懂01背包的&#xff0c;有必要了解一下。 什么是完全背包&#xff1f; 01背包问题&#xff1a;有一个背包承重为V&#xff0c;有N个物品&#xff0c;每个物品的价值(…

Codeforces Round 1003 (Div. 4)

ABCDE略 F 如果这个序列有两个一样的数挨着或者中间只隔一个其他的数&#xff0c;那么这个数就是多数。可以用反证法&#xff0c;构造一个多值序列无法不包含以上两种结构。只需要在树上找这两种结构就可以了 #include <bits/stdc.h> #define int long long using nam…

金融数据分析(MATLAB)个人学习笔记(5):金融实证分析实例

一、国内外常用金融数据库简介 &#xff08;一&#xff09;国外数据库 1. CRSP数据库 CRSP&#xff08;Center for Research in Security Prices,证券价格研究中心&#xff09;是美国芝加哥大学商研所金融研究中心的产品。收集的美国股票和指数数据来源主要为纽约证券交易所…

硬件基础(3):三极管(4):关于三极管的压降

文章目录 三极管的压降使用与测量注意事项 三极管的压降 三极管的“压降”通常是指在一定工作状态下&#xff0c;三极管不同电极之间产生的电压差。对于常见的双极性晶体管&#xff08;BJT&#xff09;而言&#xff0c;最常讨论的压降通常包括以下几个部分&#xff1a; 基-发射…

[深度学习]图像分类项目-食物分类

图像分类项目-食物分类(监督学习和半监督学习) 文章目录 图像分类项目-食物分类(监督学习和半监督学习)项目介绍数据处理设定随机种子读取文件内容图像增广定义Dataset类 模型定义迁移学习 定义超参Adam和AdamW 训练过程半监督学习定义Dataset类模型定义定义超参训练过程 项目介…

5.go切片和map

切片的概念 数组和切片相比较切片的长度是不固定的&#xff0c;可以追加元素&#xff0c;在追加时可能会使切片的容量增大&#xff0c;所以可以将切片理解成 "动态数组"&#xff0c;但是&#xff0c;它不是数组&#xff0c;而是构建在数组基础上的更高级的数据结构。…

在 Windows 上安装 PowerShell 的多种方法与完整指南

原文&#xff1a;在 Windows 上安装 PowerShell 的多种方法与完整指南 | w3cschool笔记 在 Windows 上安装 PowerShell 有多种方式。每种安装方法都适用于不同的场景和工作流。请选择最适合您需求的方法。 WinGet&#xff1a;推荐在 Windows 客户端上安装 PowerShell 的方式MS…

云原生算力引擎:分布式推理的流体动力学

引言&#xff1a;算力黑洞的引力扰动 OpenAI推理集群日处理4.5亿次请求&#xff0c;CUDA 12.3实现μs级张量切换。特斯拉Dojo超算芯片间延迟0.5ns&#xff0c;阿里巴巴PAI平台节省58%训练时长。HuggingFace模型库下载量突破3亿次&#xff0c;AWS Inferentia芯片能效比提升8倍。…

MySQL MVCC的快照读和当前读区别,Redis的RDB+AOF混合持久化流程。

MySQL MVCC 的快照读和当前读区别 快照读 (Snapshot Read) 定义: 读取数据的历史版本&#xff08;快照&#xff09;&#xff0c;基于 MVCC&#xff08;多版本并发控制&#xff09;实现。特点: 不加锁&#xff0c;非阻塞读。返回事务开始时的快照数据&#xff0c;确保一致性。…

Cesium 自定义路径导航材质

cesium 自定义路径导航纹理图片随便更换&#xff0c;UI 提供设计图片即可达到效果&#xff1b; 打开小马的weix 关注下 搜索“技术链” 回复关键词《《路径》》获取原始代码&#xff1b; 拿到就能用轻松解决&#xff01;帮忙点个关注吧&#xff01;

3月25号

添加图片的一些例子: // 创建一个二维数组,用来管理数据int[][] data new int[4][4]; // 记录空白方块的位置int x0;int y0; // 定义一个变量,记录当前展示图片的路径String path"E:\\java\\jigsawgame\\路飞\\路飞"; // 加载图片细节: // …

【机器学习】什么是支持向量机?

什么是支持向量机&#xff1f; 支持向量机&#xff08;SVM&#xff0c;Support Vector Machine&#xff09;是一种强大的机器学习算法&#xff0c;常用于分类问题&#xff0c;也可以用于回归问题。它的核心思想是通过找到一个最佳的“超平面”来将不同类别的数据分开&#xff…

10分钟打造专属AI助手!ToDesk云电脑/顺网云/海马云操作DeepSeek哪家强?

文章目录 一、引言云计算平台概览ToDesk云电脑&#xff1a;随时随地用上高性能电脑 二 .云电脑初体验DeekSeek介绍版本参数与特点任务类型表现 1、ToDesk云电脑2、顺网云电脑3、海马云电脑 三、DeekSeek本地化实操和AIGC应用1. ToDesk云电脑2. 海马云电脑3、顺网云电脑 四、结语…