提示:每日催眠:写代码使我快乐。
题目
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
解题思路及代码
思路
这个题我们可以转化成两数之和,再按照跷跷板原理,就可以很快的解决,但是问题的关键在于去重,我们可以先进行排序,从开头取数字,取完后要确保后一个数字和所取数字不同,然后定义左右指针,通过判断取到的数字、左指针数字、右指针数字三个数字之和是否为0,等于0的情况下我们还不能直接加入结果里,我们要确保左指针指向的最右边一个重复的数字以及右指针指向最左边一个重复的数字,这样的话就不会出现结果重复(切记更新指针,不然会死循环的)。如果小于0的话说明左边小了,往后移动一个,如果大于0说明右边大了,往前移动一个,这样我们就可以顺利解决问题。这个地方我们有个小技巧来优化程序,通过排序,我们可以得出小的数字在前面,要想三个数之和为0,第一个数一定要小于0,那么我们设置跳出循环条件在最前面来优化程序。
代码
代码如下(C++):
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int l = i + 1;
int r = nums.size() - 1;
while (l < r) {
int num = nums[i] + nums[l] + nums[r];
if (num == 0) {
while (l + 1 < nums.size() && nums[l] == nums[l + 1]) {
l++;
}
while (r - 1 > 0 && nums[r] == nums[r - 1]) {
r--;
}
vector<int> ans;
ans.push_back(nums[i]);
ans.push_back(nums[l]);
ans.push_back(nums[r]);
res.push_back(ans);
l++;
r--;
} else if (num < 0) {
l++;
} else if (num > 0) {
r--;
}
}
}
return res;
}
};
总结
主要的思路就是跷跷板和去重,用结构体+哈希+三重暴力循环(别问我怎么知道,面试的时候写的,关键是最后一重循环条件写错了,我真的服了我自己了,错失了offer,哭泣)也是可以的,但是时间肯定爆了,尽量不要暴力吧,会很不能体现你的逻辑性。(题外话:我未来的男朋友,你怎么还不给我表白,是在等我表白吗?)