题目描述
思路
思路1
比较容易想到的就是,求三数之和等于0,可以等价于求两个数的和,然后看这个和的相反数是否在nums
里面。
但是 T_T这样的话复杂度太高了,会超时,捂脸,最后三个case,怎么改都超时……
bool find(vector<int> nums, int nums_sz, int target, int m, int n){
for (int i = 0; i < nums_sz; i++){
if (i == m || i == n)
continue;
if (nums[i] == target)
return true;
}
return false;
}
vector<vector<int>> threeSum(vector<int>& nums) {
int nums_sz = nums.size();
vector<vector<int>> res;
set<vector<int>> s;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums_sz - 1; i++){
int start = nums[i];
for (int j = i + 1; j < nums_sz; j++){
int end = nums[j];
int opposite_number = -(start + end);
if (find(nums, nums_sz, opposite_number, i, j)){
vector<int> tmp(NUM);
tmp[0] = start;
tmp[1] = end;
tmp[2] = opposite_number;
sort(tmp.begin(), tmp.end());
s.insert(tmp);
}
}
}
int s_sz = s.size();
for (set<vector<int>>::iterator it = s.begin(); it != s.end(); it++){
res.push_back(*it);
}
return res;
}
思路2
最后没办法去网上找了下人家的sol,还有这篇(没有优化),发现是我之前的思路不够好,而且没有优化到很好的地步。
总体思路是先排序(从小到大),然后以每一个数为起始,在它的右边不断缩小范围看是否有符合条件的数。
首先需要将原数组排序,这里可以用c++的sort接口就行:
sort(nums.begin(), nums.end());
这么排序我觉得个好处是当找到满足条件的三元组时,不需要判断结果的二维vector中是否已经包含了当前要放进去的子数组;然后对于当前的数
nums[i]
,在他的右边用下标left
和right
进行缩小范围的遍历;- 如果
sum = nums[i] + nums[left] + nums[right]; > 0
,那么说明nums[right]
大了,因此right--
;如果sum<0
,那么说明nums[left]
小了,因此left++
; - 1-3为基本的想法,然后在此之上还要有优化,跳过一些不必要的循环,还有提前退出(见代码)
代码
vector<vector<int>> threeSum(vector<int>& nums) {
int nums_sz = nums.size();
vector<vector<int>> res;
sort(nums.begin(), nums.end()); // 排序
int left, right, sum;
vector<int> tmp(NUM);
for (int i = 0; i < nums_sz - 2; i++){ // 这里只需要到nums_sz-3的位置即可,后面还有 nums_sz-2, nums_sz-1
left = i + 1;
right = nums_sz - 1;
// 优化1: 当nums[i] == nums[i - 1]时,nums[i-1]与nums[i+1]--nums[nums_sz-1]的组合情况 和 nums[i]的情况一致,
// 因此可以跳过nums[i]的循环,i>0保证i-1不会访问越界,同时也保证了输入为[0,0,0]时的正确性
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 优化2: 当nums[i] > 0 ,那么nums[i]加上后面的比它大的两个数肯定也大于0,甚至是后面的数([i+1], [i+2]……)作为起始数时肯定也大于0(数组已经排过序),
// 因此可以跳过;
// 当 nums[i] + nums[left] + nums[left+1]>0,说明nums[i]与后面的范围中任取两个数并且这两个数是最小值,他们的和都大于0,后面的数([i+1], [i+2]……)作为起始数时sum也大于0,
// 因此也可以跳过;
if (nums[i] > 0 || (nums[i] + nums[left] + nums[left+1])>0){
break;
}
// 优化3: 当nums[i] + nums[right] + nums[right - 1] < 0时,说明nums[i]与后面的范围中任取两个数并且这两个数是最大值,得到的sum < 0,说明nums[i]这个起始数选小了
// 因此跳过进入下一层循环;
if (nums[i] + nums[right] + nums[right - 1] < 0) {
continue;
}
while (left < right){
sum = nums[i] + nums[left] + nums[right];
if (sum == 0){
tmp[0] = nums[i];
tmp[1] = nums[left];
tmp[2] = nums[right];
res.push_back(tmp);
left++;
right--;
// 优化4: 如果nums[left] == nums[left - 1](left变化之前),那么此时nums[left]--nums[right]这个范围得到的结果还是和nums[left-1]--nums[right]得到的一样
// 下面同理;
while (left < right && nums[left] == nums[left - 1]){
left++;
}
while (left < right && nums[right] == nums[right + 1]){
right--;
}
tmp.clear();
tmp.resize(NUM);
}
else if (sum < 0){
left++;
while (left < right && nums[left] == nums[left - 1]){
left++;
}
}
else if (sum > 0){
right--;
while (left < right && nums[right] == nums[right + 1]){
right--;
}
}
}
}
return res;
}
以后做题一定要多想想有没有更好的办法,还得多注意下能不能优化的。切记切记!