leetcode15—三数之和
关键词:双指针 排序
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2], [-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
解法—双指针
动画说明见 这里
先对数组进行排序方便比较,因为题目要求的数而不是下标所以可以进行排序。
- 在遍历数组的过程中,对每个数 i 定义一个left指针和right指针。
- 固定i,根据三者的和对left和right进行移动,
- 当left超过right退出,移动 i 并定义新的left指针和right指针。
同时还要考虑重复数字的情况,因此移动的时候要移动到一个新的数的位置。不然会产生相同的解,不满足题意。
代码
// 快速排序
function sortarray(nums) {
function fastsort(low, high) {
if (low>=high) return
let mid = low;
for (let i=low+1; i<=high; i++){
if (nums[i] < nums[mid]) {
let temp = nums[i];
nums[i] = nums[mid];
nums[mid] = temp;
++mid;
temp = nums[mid];
nums[mid] = nums[i];
nums[i] = temp;
}
}
fastsort(low, mid-1);
fastsort(mid+1, high);
}
if (nums.length<=1) return nums
fastsort(0, nums.length-1);
return nums;
}
var threeSum = function(nums) {
if (nums.length<3) return [];
nums = sortarray(nums);
res = [];
let left, right;
for (let i=0; i<nums.length-2; i++) {
// i大于0表示三个数都大于0,因此和不可能为0
if (nums[i]>0) break;
// i对应数与上一个相同的话跳过
// 特殊情况是有三个0,通过i大于等于1使数组只有三个零时正常执行
// 与之前的相同跳过使得相同情况下也起码会执行一轮判断
if (i>=1 && nums[i] == nums[i-1]) continue;
left = i+1;
right = nums.length - 1;
while (left<right) {
target = nums[i] + nums [left] + nums[right];
if (target==0) {
res.push([nums[i], nums[left], nums[right]]);
// 处理重复情况 避免相同解两侧都要进行移动
// 因为上一组刚好为0了,所以在无重复解而且i固定的前提下,移动一个数无法获得满足题意的解
// 必须要两边都进行移动
while (left<right && nums[left]==nums[left+1]) ++left;
while (left<right && nums[right]==nums[right-1]) --right;
--right;
++left;
}
else if (target > 0) {
// 处理重复情况
while (left<right && nums[right]==nums[right-1]) --right;
--right;
}
else {
// 处理重复情况
while (left<right && nums[left]===nums[left+1]) ++left;
++left;
}
}
}
return res;
};
这里练习了一下快速排序,已经有点生疏了。
代码的流程用注释进行了解释。
一些优化处理:
// i大于0表示三个数都大于0,因此和不可能为0
if (nums[i]>0) break;
同时不能忘记 i 自身的迭代也会重复:
// i对应数与上一个相同的话跳过
// 特殊情况是有三个0,通过i大于等于1使数组只有三个零时正常执行
// 与之前的相同跳过使得相同情况下也起码会执行一轮判断
if (i>=1 && nums[i] == nums[i-1]) continue;
又要考虑三个零也能满足题意的情况,加上i大于等于1并与前一个比较的条件。
移动时也要注意在三数之和等于0时,要同时移动左右指针,因为只移动一个会明显不相等因此两个都要移动。
// 处理重复情况 避免相同解两侧都要进行移动
// 因为上一组刚好为0了,所以在无重复解而且i固定的前提下,移动一个数无法获得满足题意的解
// 必须要两边都进行移动
while (left<right && nums[left]==nums[left+1]) ++left;
while (left<right && nums[right]==nums[right-1]) --right;
--right;
++left;
外层的移动是因为内部的while循环还不足以移动到新的数字,会停在前一位,因此还需再移动一次。
复杂度分析:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2), for循环时间复杂度是 O ( n ) O(n) O(n),while循环也是 O ( n ) O(n) O(n)。
- 空间复杂度: O ( log n ) O(\log n) O(logn),不考虑返回答案的数组的空间,排序使用的空间是 O ( log n ) O(\log n) O(logn),如果不能在原数组上修改的话,则排序到新的数组的复杂度就是 O ( n ) O(n) O(n)。