18-四数之和
题目:
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
解答:
自己解答代码:
vector<vector<int>> fourSum(vector<int>& nums, int target) { vector<vector<int>> result; sort(nums.begin(), nums.begin() + nums.size()); auto isExist = [&](vector<int>& vec) { for (auto val : result) { if (val.at(0) != vec.at(0)) continue; if (val.at(1) != vec.at(1)) continue; if (val.at(2) != vec.at(2)) continue; if (val.at(3) != vec.at(3)) continue; return true; } return false; }; int idx1 = 0, idx2; for (int i = 0; i < nums.size(); i++) { if (i > 0 && nums.at(i) == nums.at(i - 1))//跟上一个重复,不处理 continue; for (int j = i+1; j < nums.size(); j++) { idx1 = j + 1; idx2 = nums.size() - 1; while (idx1 < idx2) { if (idx1 > j + 1 && nums.at(idx1) == nums.at(idx1 - 1)) { idx1++; continue; } int val = nums.at(i) + nums.at(j) + nums.at(idx1) + nums.at(idx2); if (val < target) { idx1++; } else if (val > target) { idx2--; } else { vector<int> vec = { nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) }; bool has = isExist(vec); if (!has) result.push_back({ nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) }); idx1++; idx2--; } } } } return result; }
问题:1.去重部分太复杂,使用lambda表达式每次遍历一遍是否重复,太耗时
参考三数之和的去重,解答如下:
vector<vector<int>> fourSum2(vector<int>& nums, int target) { vector<vector<int>> result; sort(nums.begin(), nums.begin() + nums.size()); int idx1 = 0, idx2; for (int i = 0; i < nums.size(); i++) { if (i > 0 && nums.at(i) == nums.at(i - 1))//只有上一个是找到的才跳过,跟上一个重复,不处理 continue; for (int j = i + 1; j < nums.size(); j++) { if (j > i + 1 && nums.at(j) == nums.at(j - 1)) { continue;//这里的j不用j++了,因为continue后j就自动++了 } idx1 = j + 1; idx2 = nums.size() - 1; while (idx1 < idx2) { if (idx1 > j + 1 && nums.at(idx1) == nums.at(idx1 - 1)) { idx1++; continue; } int val = nums.at(i) + nums.at(j) + nums.at(idx1) + nums.at(idx2); if (val < target) { idx1++; } else if (val > target) { idx2--; } else { result.push_back({ nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) }); idx1++; idx2--; } } } } return result; }
问题:整个代码的主体流程与解答一致,但是运行速度较差,比较了解答,发现有许多判断条件用于减少循环次数,修改代码如下:
vector<vector<int>> fourSum3(vector<int>& nums, int target) { vector<vector<int>> result; if (nums.size() < 4) //如果输入小于4个则直接返回空vec return result; sort(nums.begin(), nums.begin() + nums.size()); int idx1 = 0, idx2; for (int i = 0; i < nums.size(); i++) { if (i > 0 && nums.at(i) == nums.at(i - 1))//只有上一个是找到的才跳过,跟上一个重复,不处理 continue; //在这里判断是否有继续执行的必要,即i i+1 i+2 i+3之和大于target则退出循环 if (i + 3 < nums.size() && (nums.at(i) + nums.at(i + 1) + nums.at(i + 2) + nums.at(i + 3) > target)) break; for (int j = i + 1; j < nums.size(); j++) { if (j > i + 1 && nums.at(j) == nums.at(j - 1)) { continue;//这里的j不用j++了,因为continue后j就自动++了 } //i加上j j+1 j+2如果大于target则直接break if ((j+2)<nums.size() && nums.at(i) + nums.at(j) + nums.at(j + 1) + nums.at(j + 2) > target) break; idx1 = j + 1; idx2 = nums.size() - 1; while (idx1 < idx2) { int val = nums.at(i) + nums.at(j) + nums.at(idx1) + nums.at(idx2); if (val < target) { idx1++; } else if (val > target) { idx2--; } else { result.push_back({ nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) }); while (idx1 < idx2 && nums.at(idx1) == nums.at(idx1 + 1)) { idx1++; } while (idx1 < idx2 && nums.at(idx2) == nums.at(idx2 - 1)) { idx2--; } idx1++; idx2--; } } } } return result; }
总结:
1.同样的算法,一些预处理、判断条件的不同,最终运行速度也会差的很大;
2.在循环中对i j++时要注意,因为for每次循环后都会自动++,而while需要手动++,注意针对不同情况的写法,很容易引入bug。