四数之和
- 一、题目链接
- 二、题目
- 三、题目解析
- 四、算法原理
- 解法一:排序 + 暴力枚举 + 利用 set 去重
- 解法二:排序 + 双指针
- 五、编写代码
- 六、时间复杂度和空间复杂度
一、题目链接
四数之和
二、题目
三、题目解析
题目要求基本与三数之和一样。
四、算法原理
解法几乎照搬三数之和
解法一:排序 + 暴力枚举 + 利用 set 去重
绝对超时,时间复杂度是O(n^4)。
解法二:排序 + 双指针
示例1排好序:
步骤:
- 排序
- 从左向右依次固定一个数a
- 在a的右区间内,利用"三数之和"找到三个数,使这三个数之和等于target - a即可:从左向右依次固定一个数b,在b的右区间内利用"双指针"找到两个数,使这两个数之和等于target - a - b即可。
代码大体样子:两层for循环,中间套了一个双指针算法。不过这只是大体样子,还需要解决和"三数之和"同样的细节问题:结果不重且不漏。
不重:"三数之和"仅有两个地方。因为这里多了一个数,所以有3个地方要保证不重
- 找到一个四元组结果,left和right都要跳过重复元素
- 每使用完一遍"双指针",b也跳过重复元素
- b每遍历完一遍,a也跳过重复元素
不漏:"双指针"找到一结果就缩小区间。
五、编写代码
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;// 1.排序sort(nums.begin(), nums.end());// 2.固定数a,固定数b,双指针算法int n = nums.size();for (int i = 0; i < n; )// 固定数a{// 三数之和for (int j = i + 1; j < n; )// 固定数b{// 双指针int aim = target - nums[i] - nums[j];int left = j + 1, right = n - 1;while (left < right){int sum = nums[left] + nums[right];if (sum > aim) --right;else if (sum < aim) ++left;else{// 找到一结果就push_back,保证不漏缩小区间ret.push_back({ nums[i], nums[j], nums[left++], nums[right--]});// 去重 left和rightwhile (left < right && nums[left] == nums[left - 1]) ++left;while (left < right && nums[right] == nums[right + 1]) --right;}}// 去重j++j;while (j < n && nums[j] == nums[j - 1]) ++j;}// 去重i++i;while (i < n && nums[i] == nums[i - 1]) ++i;}return ret;}
};
数据范围溢出的风险,计算的时候可能会超出int的范围:
可以用long long
类型的变量存储,后面的计算过程只用对第一个数据/第二个数据强制转换:
long long aim = (long long)target - nums[i] - nums[j];
long long aim = target - (long long)nums[i] - nums[j];
完整代码:
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;// 1.排序sort(nums.begin(), nums.end());// 2.固定数a,固定数b,双指针算法int n = nums.size();for (int i = 0; i < n; )// 固定数a{// 三数之和for (int j = i + 1; j < n; )// 固定数b{// 双指针long long aim = target - (long long)nums[i] - nums[j];if (nums[i] > 0 && nums[j] > 0 && target < 0) break; int left = j + 1, right = n - 1;while (left < right){int sum = nums[left] + nums[right];if (sum > aim) --right;else if (sum < aim) ++left;else{// 找到一结果就push_back,保证不漏缩小区间ret.push_back({ nums[i], nums[j], nums[left++], nums[right--]});// 去重 left和rightwhile (left < right && nums[left] == nums[left - 1]) ++left;while (left < right && nums[right] == nums[right + 1]) --right;}}// 去重j++j;while (j < n && nums[j] == nums[j - 1]) ++j;}// 去重i++i;while (i < n && nums[i] == nums[i - 1]) ++i;}return ret;}
};
六、时间复杂度和空间复杂度
for循环嵌套for循环嵌套while循环,所以时间复杂度是O(n^3)。
空间复杂度依旧是排序算法占空间,所以空间复杂度是O(logn)。