原题:牛客三数之和
题目大意
给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。
分析
1.首先将该数组进行快速排序,得到一个非降序数组,利于后续从左到右查找。
2.遍历该数组使用Map得到每个数出现的次数。
3.遍历该数组,假设当前遍历的值为一个解答的最小数,即为图中的i位置,现在问题转换为是否存在两个位于i之后的数,使得它们之和为0,。
4.设left为i+1,假定left为可能解的较小值,则可得arr[left]需小于-arr[i]/2,所以可在该范围内遍历left。若存在某个值= -(arr[i]+arr[left]) ,则满足要求,将它push入解集中。
关键点
1.当遍历到i位置时,应当在map中对该key对应的值-1,以免干扰结果。
2.当遍历到i位置时,若arr[i]==arr[i-1],则continue。
3.当arr[left]=arr[i]+arr[left]时,即arr[left]=arr[i]/2。需要判断arr[left]在数组i位置后出现的次数,若>=2,则满足条件。
js代码
//快速排序
function quickSort( arr ) {
if(arr.length <= 1) return arr;
const tar = arr[0];
let left = [],right = [];
for(let i = 1;i< arr.length;i++) {
if(arr[i] <= tar) left.push(arr[i]);
else right.push(arr[i]);
}
return quickSort(left).concat([tar],quickSort(right));
}
function threeSum( num ) {
if(num.length<3) return [];
let arr = quickSort(num);
let mp = new Map();
let res = [];
arr.forEach(function(value,index,arr) {
if(mp.has(value)) mp.set(value,mp.get(value)+1);
else mp.set(value,1);
})
for(let i =0;i<arr.length;i++) {
if(arr[i]>0) break;
if(i>0&&arr[i]==arr[i-1]) continue;
mp.set(arr[i],mp.get(arr[i])-1);
let left = i+1,maxn = Math.floor(-arr[i]/2);
while(arr[left]<=maxn) {
if(left>i+1&&arr[left]==arr[left-1]) {
left++;
continue;
}
else if(arr[left] == -(arr[i]+arr[left]) && mp.get(arr[left])>=2) {
res.push([arr[i],arr[left],arr[left]]);
}
else if(mp.has(-(arr[i]+arr[left])) &&arr[left] != -(arr[i]+arr[left])) {
res.push([arr[i],arr[left],-(arr[i]+arr[left])]);
}
left++;
}
}
return res;
}