- C++
C++ 双指针算法
- 2025-5-13 20:33:07 @
🧠C++ 双指针算法教程(0基础入门 | 通俗易懂)
双指针算法是一种非常实用且高效的技巧,常用于数组、链表等数据结构中。本教程将从零开始,带你掌握双指针的基本思想,并通过多个简单实例帮助你熟练使用它。
📚目录
✨什么是双指针?
双指针指的是在程序中使用两个“指针”来遍历或操作数组/链表中的元素。这里的“指针”可以是:
- 实际的指针变量
- 数组下标(int 类型)
- 容器迭代器(如 vector 的 iterator)
💡核心思想:通过两个指针的移动来减少时间复杂度,通常将 O(n²) 的问题优化到 O(n)。
💡为什么用双指针?
- ❗避免暴力枚举,提升效率
- ❗逻辑清晰,代码简洁
- ❗适用于多种场景:去重、排序、查找目标值等
🔍双指针的常见类型
1️⃣ 同向双指针(快慢指针)
- 两个指针从同一方向出发
- 常用于删除重复元素、链表操作等
2️⃣ 对撞双指针
- 指针分别从数组两端出发,向中间靠拢
- 常用于有序数组中找两数之和、三数之和等
3️⃣ 滑动窗口
- 一种特殊的双指针,维护一个区间窗口
- 常用于求子数组的最大和、最小长度等
🎯实战练习:经典例题讲解
我们使用 using namespace std;
来简化代码书写。
✅例题1:移除数组中的重复元素(同向双指针)
给定一个有序数组
nums
,请你原地移除重复元素,使得每个元素只出现一次。
示例:
输入: nums = [1,1,2]
输出: 2, nums = [1,2,_]
✅代码如下:
#include <iostream>
#include <vector>
using namespace std;
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]) {
++slow;
nums[slow] = nums[fast];
}
}
return slow + 1;
}
int main() {
vector<int> nums = {1, 1, 2};
int len = removeDuplicates(nums);
cout << "新长度为:" << len << endl;
cout << "修改后的数组为:";
for (int i = 0; i < len; ++i) {
cout << nums[i] << " ";
}
return 0;
}
输出结果:
新长度为:2
修改后的数组为:1 2
✅例题2:有序数组中找出两数之和等于目标值(对撞双指针)
给定一个升序排列的整数数组
nums
和一个目标值target
,请你在该数组中找出两个数,使它们的和等于target
。
示例:
输入: nums = [2,7,11,15], target = 9
输出: [0,1]
✅代码如下:
#include <iostream>
#include <vector>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
return {left, right};
} else if (sum < target) {
++left;
} else {
--right;
}
}
return {-1, -1}; // 没找到
}
int main() {
vector<int> nums = {2, 7, 11, 15};
int target = 9;
vector<int> res = twoSum(nums, target);
if (res[0] != -1) {
cout << "找到两数索引:" << res[0] << " 和 " << res[1] << endl;
} else {
cout << "未找到符合条件的两个数" << endl;
}
return 0;
}
输出结果:
找到两数索引:0 和 1
🧪总结与练习建议
✅双指针的优势:
特点 | 描述 |
---|---|
时间复杂度 | 一般为 O(n),优于暴力 O(n²) |
空间复杂度 | 原地操作,O(1) |
适用场景 | 数组、链表、滑动窗口、有序结构等 |
📝课后练习推荐
- 【简单】LeetCode 26. 删除有序数组中的重复项
- 【简单】LeetCode 167. 两数之和 II - 输入有序数组
- 【中等】LeetCode 11. 盛最多水的容器
- 【中等】LeetCode 3. 无重复字符的最长子串
🏁结语
双指针是一种非常实用的编程技巧,适合初学者快速上手并写出高效代码。掌握它的关键是多做练习、理解指针移动的逻辑!
🎯记住一句话:
“双指针不是魔法,而是思维的艺术。”
2 条评论
-
admin SU @ 2025-5-13 20:35:22
C++双指针算法教程
双指针算法是一种高效的算法技巧,广泛应用于数组、字符串和链表等线性数据结构的问题中。本教程将从基础概念开始,逐步引导你掌握双指针算法的核心思想和应用场景。
1. 什么是双指针算法?
双指针算法是指使用两个指针(变量)同时遍历数据结构,通过合理移动指针的位置来解决问题。与普通的单指针遍历相比,双指针可以大幅降低时间复杂度,提高算法效率。
双指针的核心思想:
通过两个指针的协作,将原本需要嵌套循环的问题转化为单次遍历,从而将时间复杂度从 O(n²) 优化到 O(n)。2. 双指针的常见类型
双指针算法主要分为以下几种类型:
(1) 对撞指针(Two Pointers Opposite Direction)
- 特点: 两个指针分别从数组的两端向中间移动。
- 适用场景: 有序数组、查找配对、回文判断等。
(2) 快慢指针(Two Pointers Fast and Slow)
- 特点: 两个指针以不同的速度移动,通常快指针每次移动两步,慢指针每次移动一步。
- 适用场景: 链表环检测、中位数查找、删除重复元素等。
(3) 滑动窗口(Sliding Window)
- 特点: 两个指针形成一个窗口,通过移动窗口的左右边界来解决区间问题。
- 适用场景: 子数组求和、字符串匹配、最长子串等。
3. 对撞指针示例:两数之和
问题描述:
给定一个有序数组nums
和一个目标值target
,找到数组中两个数的和等于target
,返回它们的下标。示例代码:
#include <iostream> #include <vector> using namespace std; vector<int> twoSum(vector<int>& nums, int target) { int left = 0; // 左指针,指向数组开头 int right = nums.size() - 1; // 右指针,指向数组末尾 while (left < right) { int sum = nums[left] + nums[right]; if (sum == target) { return {left, right}; // 找到解,返回下标 } else if (sum < target) { left++; // 和太小,左指针右移 } else { right--; // 和太大,右指针左移 } } return {-1, -1}; // 无解 } int main() { vector<int> nums = {2, 7, 11, 15}; int target = 9; vector<int> result = twoSum(nums, target); cout << "两数的下标分别为: " << result[0] << " 和 " << result[1] << endl; return 0; }
输出结果:
两数的下标分别为: 0 和 1
算法思路:
- 初始化左指针
left
指向数组开头,右指针right
指向数组末尾。 - 计算两指针所指元素的和
sum
:- 如果
sum == target
,则找到解,返回下标。 - 如果
sum < target
,说明需要更大的和,左指针右移。 - 如果
sum > target
,说明需要更小的和,右指针左移。
- 如果
- 重复上述过程直到找到解或指针相遇。
4. 快慢指针示例:删除有序数组中的重复项
问题描述:
给定一个有序数组nums
,原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。示例代码:
#include <iostream> #include <vector> using namespace std; 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]) { slow++; nums[slow] = nums[fast]; // 将不重复元素移动到慢指针位置 } } return slow + 1; // 新数组长度为慢指针位置加1 } int main() { vector<int> nums = {1, 1, 2, 2, 3, 4, 4, 4, 5}; int newLength = removeDuplicates(nums); cout << "删除重复项后的数组长度为: " << newLength << endl; cout << "删除重复项后的数组内容为: "; for (int i = 0; i < newLength; i++) { cout << nums[i] << " "; } cout << endl; return 0; }
输出结果:
删除重复项后的数组长度为: 5 删除重复项后的数组内容为: 1 2 3 4 5
算法思路:
- 初始化慢指针
slow
指向数组第一个元素。 - 使用快指针
fast
遍历数组:- 如果
nums[fast]
不等于nums[slow]
,说明遇到了新元素,将slow
指针后移一位,并将nums[fast]
的值赋给nums[slow]
。
- 如果
- 遍历结束后,
slow + 1
即为新数组的长度。
5. 滑动窗口示例:最大连续子数组和
问题描述:
给定一个整数数组nums
,找到一个具有最大和的连续子数组,返回其最大和。示例代码:
#include <iostream> #include <vector> #include <algorithm> using namespace std; int maxSubArray(vector<int>& nums) { if (nums.empty()) return 0; int currentSum = nums[0]; // 当前窗口的和 int maxSum = nums[0]; // 最大和 for (int i = 1; i < nums.size(); i++) { // 如果当前窗口的和为负数,丢弃当前窗口,从新元素开始 currentSum = max(nums[i], currentSum + nums[i]); // 更新最大和 maxSum = max(maxSum, currentSum); } return maxSum; } int main() { vector<int> nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int maxSum = maxSubArray(nums); cout << "最大连续子数组和为: " << maxSum << endl; return 0; }
输出结果:
最大连续子数组和为: 6
算法思路:
- 初始化当前窗口的和
currentSum
和最大和maxSum
为数组的第一个元素。 - 从第二个元素开始遍历数组:
- 对于每个元素,判断是将其加入当前窗口(
currentSum + nums[i]
)还是重新开始一个新窗口(nums[i]
),取较大值更新currentSum
。 - 每次更新
currentSum
后,比较其与maxSum
的大小,更新maxSum
。
- 对于每个元素,判断是将其加入当前窗口(
- 遍历结束后,
maxSum
即为所求的最大连续子数组和。
6. 双指针算法的应用场景
双指针算法适用于以下场景:
- 有序数组/链表:对撞指针常用于有序数组的查找问题。
- 数组去重/删除元素:快慢指针可高效处理此类问题。
- 子数组/子串问题:滑动窗口是解决这类问题的利器。
- 环检测:快慢指针可检测链表中是否存在环。
7. 练习题
- 反转字符串:编写一个函数,将输入的字符串反转。
- 判断回文串:给定一个字符串,判断它是否是回文串(忽略大小写和非字母数字字符)。
- 盛最多水的容器:给定 n 个非负整数,每个数代表坐标中的一个点 (i, ai),找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
总结
双指针算法是一种高效且实用的算法技巧,通过合理运用对撞指针、快慢指针和滑动窗口,可以解决许多复杂的线性数据结构问题。掌握双指针算法的关键在于理解问题的特性,并选择合适的指针策略。通过不断练习,你将能够熟练运用双指针算法解决各种问题。
-
2025-5-13 20:34:51@
C++双指针算法入门教程
双指针算法是一种高效的算法技巧,广泛应用于数组、链表等线性数据结构的问题中。它通过使用两个指针在数据结构中协同移动,从而在不使用额外空间或减少时间复杂度的情况下解决问题。
1. 双指针算法基本概念
双指针算法通常包含以下两种形式:
- 对撞指针:两个指针分别从数组的两端开始向中间移动
- 快慢指针:两个指针以不同的速度在数组或链表中移动
2. 对撞指针示例:两数之和
问题描述:给定一个已排序的数组和一个目标值,找到数组中两个数的和等于目标值,并返回它们的下标。
解决思路:使用对撞指针,一个指针指向数组的开始,另一个指针指向数组的末尾。计算两个指针所指元素的和,如果和等于目标值,则返回下标;如果和小于目标值,则将左指针右移;如果和大于目标值,则将右指针左移。
下面是解决该问题的C++代码:
#include <iostream> #include <vector> using namespace std; // 使用标准命名空间,避免重复写std:: vector<int> twoSum(vector<int>& numbers, int target) { int left = 0; int right = numbers.size() - 1; while (left < right) { int sum = numbers[left] + numbers[right]; if (sum == target) { return {left + 1, right + 1}; // 题目要求下标从1开始 } else if (sum < target) { left++; // 和太小,左指针右移 } else { right--; // 和太大,右指针左移 } } return {}; // 没有找到符合条件的元素 } int main() { vector<int> numbers = {2, 7, 11, 15}; int target = 9; vector<int> result = twoSum(numbers, target); if (result.size() == 2) { cout << "下标分别为: " << result[0] << " 和 " << result[1] << endl; } else { cout << "没有找到符合条件的元素" << endl; } return 0; }
代码解释:
- 函数
twoSum
接收一个已排序的整数数组和一个目标值作为参数 - 使用两个指针
left
和right
分别指向数组的开始和末尾 - 在每次循环中,计算两个指针所指元素的和,并与目标值比较
- 根据比较结果移动指针,直到找到符合条件的元素或指针相遇
3. 快慢指针示例:删除有序数组中的重复项
问题描述:给定一个已排序的数组,原地删除重复出现的元素,使得每个元素只出现一次,返回删除后数组的新长度。
解决思路:使用快慢指针,慢指针指向当前不重复的元素位置,快指针遍历数组。当快指针找到一个与慢指针不同的元素时,将该元素移动到慢指针的下一个位置,并更新慢指针。
下面是解决该问题的C++代码:
#include <iostream> #include <vector> using namespace std; // 使用标准命名空间,避免重复写std:: 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]) { slow++; nums[slow] = nums[fast]; // 将不同的元素移动到慢指针的下一个位置 } } return slow + 1; // 返回新数组的长度 } int main() { vector<int> nums = {1, 1, 2, 2, 2, 3, 4, 4, 5}; int newLength = removeDuplicates(nums); cout << "新数组长度为: " << newLength << endl; cout << "删除重复项后的数组为: "; for (int i = 0; i < newLength; i++) { cout << nums[i] << " "; } cout << endl; return 0; }
代码解释:
- 函数
removeDuplicates
接收一个已排序的整数数组作为参数 - 使用两个指针
slow
和fast
,slow
指向当前不重复的元素,fast
用于遍历数组 - 当
fast
找到一个与slow
不同的元素时,将该元素移动到slow
的下一个位置,并更新slow
- 最终返回
slow + 1
作为新数组的长度
4. 双指针算法的应用场景
双指针算法适用于以下场景:
- 数组或链表已排序
- 需要在不使用额外空间的情况下解决问题
- 需要在O(n)时间复杂度内解决问题
5. 练习题目推荐
-
反转字符串:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
-
合并两个有序数组:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
-
验证回文串:给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
通过练习这些题目,你将更好地掌握双指针算法的应用。双指针算法是解决数组和链表问题的重要技巧,掌握它将对你的算法能力有很大提升。
- 1