【C++ 双指针技巧】
文章目录
- 一、什么是双指针?
- 二、使用场景
- 1. 删除数组中重复的元素(快慢指针)
- ✅说明:
- 2. 判断有序数组中是否存在两个数之和为 target(对撞指针)
- ✅说明:
- 3. 反转字符串(对撞指针)
- ✅说明:
- 4. 判断链表是否有环(快慢指针)
- ✅说明:
- 5. 盛最多水的容器(对撞指针)
- ✅说明:
- 三、双指针与暴力法的对比优势
一、什么是双指针?
双指针,顾名思义就是使用两个指针(或索引)来遍历数据结构,它可以分为两类:
- 对撞指针(Two Pointers from Both Ends):两个指针从序列的两端向中间移动。
- 快慢指针(Fast and Slow Pointers):两个指针从一端出发,以不同的速度移动。
这种技巧可以有效地降低时间复杂度,提升算法效率。
二、使用场景
1. 删除数组中重复的元素(快慢指针)
问题描述:给定一个有序数组 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;
}int main() {vector<int> nums = {1, 1, 2, 2, 3};int len = removeDuplicates(nums);cout << "去重后长度: " << len << endl;for (int i = 0; i < len; ++i)cout << nums[i] << " ";return 0;
}
✅说明:
fast
快指针向前扫描;slow
慢指针维护已去重序列的尾部。
2. 判断有序数组中是否存在两个数之和为 target(对撞指针)
问题描述:给定一个升序数组 nums
和一个整数 target
,判断数组中是否存在两个数的和等于 target
。
#include <iostream>
#include <vector>
using namespace std;bool 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) return true;else if (sum < target) left++;else right--;}return false;
}int main() {vector<int> nums = {1, 2, 4, 7, 11, 15};int target = 15;cout << (twoSum(nums, target) ? "存在" : "不存在") << endl;return 0;
}
✅说明:
left
和right
从两端出发,寻找匹配组合;- 特别适用于有序数组。
3. 反转字符串(对撞指针)
问题描述:将字符串原地反转。
#include <iostream>
#include <vector>
using namespace std;void reverseString(vector<char>& s) {int left = 0, right = s.size() - 1;while (left < right) {swap(s[left++], s[right--]);}
}int main() {vector<char> s = {'h', 'e', 'l', 'l', 'o'};reverseString(s);for (char c : s) cout << c;return 0;
}
✅说明:
swap
的使用要特别注意++
和--
的顺序;- 通常在需要原地修改结构时非常实用。
4. 判断链表是否有环(快慢指针)
问题描述:给定一个链表,判断链表中是否有环。
#include <iostream>
using namespace std;struct ListNode {int val;ListNode* next;ListNode(int x): val(x), next(nullptr) {}
};bool hasCycle(ListNode* head) {if (!head || !head->next) return false;ListNode* slow = head;ListNode* fast = head->next;while (fast && fast->next) {if (slow == fast) return true;slow = slow->next;fast = fast->next->next;}return false;
}
✅说明:
fast
一次走两步,slow
一次走一步;- 若存在环,快慢指针终将相遇。
5. 盛最多水的容器(对撞指针)
问题描述:给定一个数组 height[]
表示每个柱子的高度,找到两个柱子之间能容纳的最大水量。
#include <iostream>
#include <vector>
using namespace std;int maxArea(vector<int>& height) {int left = 0, right = height.size() - 1;int maxWater = 0;while (left < right) {int h = min(height[left], height[right]);maxWater = max(maxWater, h * (right - left));if (height[left] < height[right]) left++;else right--;}return maxWater;
}int main() {vector<int> height = {1,8,6,2,5,4,8,3,7};cout << "最大容量: " << maxArea(height) << endl;return 0;
}
✅说明:
- 双指针在左右两端向中间靠拢;
- 每次根据高度判断该移动哪一侧。
三、双指针与暴力法的对比优势
方法 | 时间复杂度 | 适用情况 |
---|---|---|
暴力法 | O(n²) | 无序数组,无法优化 |
双指针法 | O(n) 或 O(nlogn) | 有序结构、可跳跃遍历 |
在查找两个数之和的情况下,暴力法需要两层循环,而双指针只需一次遍历。