携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情
每日刷题 2022.08.23
- leetcode原题链接:leetcode.cn/problems/1f…
- 难度:中等
- 方法: 二分
题目
- 给定一个包含 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]
输出: []
提示
0 <= nums.length <= 3000-10^5 <= nums[i] <= 10^5
解题思路
- 看到题目的第一瞬间,就联想到了两数之和那道题。那么对于本题也是一样的,确定三个数中的两个数,就可以使用二分来查找第三个数。再次查看题目描述,只需要找到不重复的三个数,不要求三个数的顺序,那么就可以先将数组排序,之后再使用二分来查找第三个数。
- 通过两个指针
i和j来确定当前需要查找的两个数,那么第三个数等于idx = 0 - nums[i] - nums[j],之后通过二分查找在数组中查找这个数。- 二分查找的区间范围:
j ~ n,为什么不是从0 ~ n呢?因为需要避免记录重复的三个数组。举例:i = -1, j = 0, idx = 1和i = -1, idx = 0, j = 1是一样的,算作重复的,只记录一个即可。
- 二分查找的区间范围:
- 后续发现还是存在重复存储的情况,例如:
-1,-1,2,0,0,1这个数组,那么-1,0,1会被记录两遍。如何处理呢?可以分析三个数,**当其中的两个数确定不重复后,那么第三个数也是一定不会重复的。**因此当遇到i或者j重复的时候,需要直接跳过,只记录一次就可以了。 - 换一个方向来想,比如数组中的
-1,-1作为i的时候是相等的,那么针对第一个-1,其往后可以组成三个数的情况,一定是包含了后面的-1往后查找的情况的,所以就无需遍历两次,避免重复。
AC代码
var threeSum = function(nums) {
nums.sort((a, b) => {
return a - b;
});
let ans = [];
let n = nums.length, q = [];
for(let i = 0; i < n; i++) {
if(i != 0 && nums[i - 1] === nums[i]) continue;
for(let j = i + 1; j < n; j++) {
if(j != (i + 1) && nums[j - 1] === nums[j]) continue;
// 还需要跳过相等的元素
let cur = 0 - (nums[i] + nums[j]);
let idx = binary(cur, j);
if(idx != j && idx != n && nums[idx] === cur) {
// 找到了
let res = [nums[i], nums[j], nums[idx]];
// 三个全部存在,说明之前计算过了
// 不存在idx并且不和前面的两个数相等的时候
if(idx === i || idx === j) break;
ans.push(res);
}
}
}
return ans;
function binary (shu, area) {
let l = area, r = n;
while(l + 1 != r) {
let mid = Math.floor((l + r) / 2);
if(nums[mid] < shu) {
l = mid;
}else {
r = mid;
}
}
return r;
}
// [-4,-1,-1,0,1,2] c
};