一、 题目描述(题目链接)
给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k,同时还满足 nums[i] + nums[j] + nums[k] == 0。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

二、 算法思路:排序 + 双指针
本题的核心难点在于降低时间复杂度以及结果去重。暴力三重循环的时间复杂度为 O(N^3),在数据量较大时会超时。采用排序 + 双指针的策略,可以将时间复杂度优化至 O(N^2)。
核心步骤
数组排序: 首先对数组进行升序排序。排序有两个关键作用:
-
便于后续使用双指针进行线性扫描。
-
便于跳过重复元素,实现结果去重。
固定第一个数(外层循环): 遍历排序后的数组,设当前索引为 i,将 nums[i] 作为三元组的第一个数。
-
剪枝优化:若 nums[i] > 0,由于数组已升序,后续数字必然大于 0,三数之和不可能为 0,直接终止循环。
-
去重:若 i > 0 且 nums[i] == nums[i-1],说明该数值已作为第一个数处理过,跳过当前循环,避免重复解。
双指针寻找另外两个数(内层循环): 在 i 之后的区间 [i+1, n-1] 中,设置左指针 left = i + 1,右指针 right = n – 1。
-
计算 sum = nums[i] + nums[left] + nums[right]。
-
若 sum == 0:找到一组解,记录结果。随后必须进行去重:左指针跳过所有重复值,右指针跳过所有重复值,最后 left++、right– 继续寻找。
-
若 sum < 0:说明和偏小,需要增大,左指针右移(left++)。
-
若 sum > 0:说明和偏大,需要减小,右指针左移(right–)。
三、 代码实现 (C++)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int n = nums.size();
// 特判:元素少于3个直接返回
if (n < 3) return result;
// 1. 排序
sort(nums.begin(), nums.end());
// 2. 遍历第一个数
for (int i = 0; i < n – 2; ++i) {
// 剪枝:第一个数大于0,后续不可能凑成和为0
if (nums[i] > 0) break;
// 去重:跳过重复的第一个数
if (i > 0 && nums[i] == nums[i-1]) continue;
// 3. 双指针寻找另外两个数
int left = i + 1;
int right = n – 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result.push_back({nums[i], nums[left], nums[right]});
// 去重:跳过重复的左指针元素
while (left < right && nums[left] == nums[left + 1]) left++;
// 去重:跳过重复的右指针元素
while (left < right && nums[right] == nums[right – 1]) right–;
// 找到解后,指针同时向内收缩
left++;
right–;
}
else if (sum < 0) {
left++; // 和太小,左指针右移
}
else {
right–; // 和太大,右指针左移
}
}
}
return result;
}
};

四、 复杂度分析
时间复杂度:O(N2)
数组排序的时间复杂度为 O(NlogN)。
外层循环遍历数组 O(N),内层双指针遍历 O(N),嵌套后为 O(N^2)。
综合来看,主导项为 O(N^2)。
空间复杂度:O(logN)
主要取决于排序算法的空间消耗。C++ 的 std::sort 通常采用内省排序(Introsort),空间复杂度为 O(logN)。除返回值外,未使用额外的线性空间。
网硕互联帮助中心


评论前必须登录!
注册