前言
在算法面试中,双指针是处理数组相关问题的利器。今天聚焦两个经典题目:三数之和与四数之和。它们都可以通过'排序 + 双指针'的策略将暴力解法的 O(n^3) 优化到 O(n^2)。
三数之和
题目描述
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。 LeetCode 链接
解题思路
这题的核心在于如何高效地找到三个数且保证不重复。
- 排序:先对数组进行排序,这样相同的数字会挨在一起,方便去重。
- 固定 + 双指针:遍历数组,固定第一个数
nums[i]。然后在i之后的区间里,用双指针left和right寻找另外两个数,使它们的和等于-nums[i]。 - 去重细节:这是最容易出错的地方。
- 如果
nums[i]和前一个数相同,说明这个位置已经处理过了,直接跳过。 - 当找到一组解后,
left右移、right左移时,也要跳过所有重复的值,防止结果集中出现重复三元组。 - 还有一个剪枝优化:如果
nums[i] > 0,因为数组已排序,后面的数肯定都大于 0,三数之和不可能为 0,可以直接结束循环。
- 如果
C++ 代码实现
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
int n = nums.size();
// 排序是双指针的前提
sort(nums.begin(), nums.end());
for (int i = 0; i < n; ) {
// 剪枝:如果当前数大于 0,后续不可能有和为 0 的组合
if (nums[i] > 0) break;
int left = i + 1, right = n - 1;
int target = -nums[i];
while (left < right) {
sum = nums[left] + nums[right];
(sum < target) {
left++;
} (sum > target) {
right--;
} {
ret.({nums[i], nums[left], nums[right]});
left++;
right--;
(left < right && nums[left] == nums[left - ]) left++;
(left < right && nums[right] == nums[right + ]) right--;
}
}
i++;
(i < n && nums[i] == nums[i - ]) i++;
}
ret;
}
};


