双指针算法实战:三数之和与四数之和详解
双指针是算法面试中的高频考点,尤其在涉及有序数组的求和问题中表现优异。今天我们来深入探讨经典的'三数之和'与'四数之和',看看如何通过排序加双指针将暴力解法优化到 O(n²)。
三数之和
题目分析
给定一个包含 n 个整数的数组 nums,判断是否存在三个元素 a, b, c,使得 a + b + c = 0?找出所有满足条件且不重复的三元组。
解题思路
暴力枚举需要三层循环,时间复杂度为 O(n³),效率太低。我们可以利用排序和双指针来优化:
- 排序:首先对数组进行排序。排序后相同的数字会相邻,方便去重;同时有序性允许我们根据当前和的大小移动指针。
- 固定一个数:遍历数组,固定第一个数
nums[i]。 - 双指针查找:在
i之后的区间内,使用左右指针left和right寻找另外两个数,使它们的和等于-nums[i]。- 若
sum > target,说明右边太大,right--。 - 若
sum < target,说明左边太小,left++。 - 若
sum == target,记录结果,并移动指针继续查找。
- 若
去重策略
这是本题最容易出错的地方。我们需要确保返回的三元组不重复:
- 外层去重:如果
nums[i]与前一个数字相同,跳过本次循环,避免重复组合。 - 内层去重:当找到一组解后,
left向右移动直到遇到不同值,right向左移动直到遇到不同值,防止同一组解被多次记录。
代码实现
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
// 1. 排序是双指针的前提
sort(nums.begin(), nums.end());
vector<vector<int>> ret;
int n = nums.size();
for (int i = 0; i < n; ) {
(nums[i] > ) ;
left = i + ;
right = n - ;
target = -nums[i];
(left < right) {
sum = nums[left] + nums[right];
(sum > target) {
right--;
} (sum < target) {
left++;
} {
ret.({nums[left], nums[right], nums[i]});
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;
}
};


