目录
1 题目描述
https://leetcode-cn.com/problems/3sum/
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。(难点)
2 输入输出
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4]
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
3 解答
1)暴力法(三层for嵌套循环)(超时)(没有代码)
2)一层for循环+双指针求twoSum(有代码)
对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum,使用双指针
难点:结果去重
去重方法:
a)对结果二维数组去重(超时):数组的项(内层数组)转为字符串,然后一维数组去重,然后再转为二维数组----原因,因为对于数组长度为n的数组arr,随机选三个数,有n(n-1)(n-2)种,如果都符合情况,那么结果数组长度为n(n-1)(n-2),去重时间复杂度为n3
b)一维数组保存结果(超时),如果数组中有该字符串(每一个结果通过,连接的字符串),最后转为二维数组
c)使用set数据结构保存结果(ac),每次add的时,会自动去重,去重时间比数组短,然后set变成二维数组(时间优化还是比较差,但是ac啦)
d)跳过去重(ac)
对于外层for循环,如果该数与前面一个相同,跳过这个数(去重),因为相同的数已经做过一遍啦。
对于内层while循环,如果左边arr[left]与前一个相同(这里不包括i项),跳过;右边类似
4 代码
ac代码
/**
* @param {number[]} arr
* @return {number[][]}
* 功能:三数之和
* 方法:对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum(三重for嵌套超时),使用双指针
* 难点:结果去重
* 难点解决方法:
3 使用set保存结果,每次add的时,会自动去重,去重时间比数组短,然后set变成二维数组(时间优化还是比较差,但是ac啦)
*/
var threeSum_1 = function(arr) {
if(arr.length <= 2){
return [];//长度小于等于2
}
arr.sort((a, b) => { //数组排序
return a - b
});
let len = arr.length;
if(arr[len - 1] < 0 || arr[0] > 0){ //排序后第一个数大于0或者最后一个小于0,返回空
return [];
}
let result = new Set(); //使用Set数据结构保存结果,操作比数组快
for(let i = 0; i < len - 2; i++){ //变量一遍数组,到倒数第三个,对于每一个数,求twoSum
let left = i + 1; //左指针
let right = len - 1; //右指针
let twoSum = 0 - arr[i]; //twoSum
while(left < right){ //左指针往右移,右指针往左移
if(arr[left] + arr[right] === twoSum){ //相等,把结果放进Set里面。
//注意,如果放进的是两个相同项的数组,如[1, 2], [1, 2];
//那么结果里面,Set有两项,因为这两个数组的地址(指向堆里面值的指针)不一样
let str = arr[i] + "," + arr[left] + "," + arr[right];
result.add(str); //自动去重,去重时间比数组快
left++;
right--;
}else if(arr[left] + arr[right] < twoSum){
left++;
}else {
right--;
}
}
}
result = [...result]; //set变成数组
for(let i = 0; i < result.length; i++){ //一维数组(项为结果组成的字符串)转为二维数组
result[i] = result[i].split(",");
for(let j = 0; j < result[i].length; j++){
result[i][j] = Number(result[i][j]);
}
}
return result;
};
/**
* @param {number[]} arr
* @return {number[][]}
* 功能:三数之和
* 方法:对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum(三重for嵌套超时),使用双指针
* 难点:结果去重(通过)
* 难点解决方法:
4 对于外层for循环,如果该数与前面一个相同,跳过这个数(去重),因为相同的数已经做过一遍啦。
对于内层while循环,如果左边arr[left]与前一个相同,跳过;右边类似
*/
var threeSum_2 = function(arr) {
if(arr.length <= 2){
return [];//长度小于等于2
}
arr.sort((a, b) => { //数组排序
return a - b
});
let len = arr.length;
if(arr[len - 1] < 0 || arr[0] > 0){ //排序后第一个数大于0或者最后一个小于0,返回空
return [];
}
let result = []; //保存结果
for(let i = 0; i < len - 2; i++){ //遍历一遍数组,到倒数第三个,对于每一个数,求twoSum
if( i- 1 >= 0 && arr[i] === arr[i - 1]){ //如果该项与前面一项一样,那么跳过该项,去重
//这里不能与后一项比较,这样不能保证跳过相同的数字,比如123334, 和为10,
//如果与后一项比较(即使这个判断放在代码最后不行,原因有三个或者三个以上相同数字时),
// 那么会跳过前面两个三,得不到结果
//i++; //这里注意啦,不能写i++,因为for循环里面已经写了,再写就重复啦。
continue;
}
let left = i + 1; //左指针
let right = len - 1; //右指针
let twoSum = 0 - arr[i]; //twoSum
//双指针求和部分
while(left < right){ //左指针往右移,右指针往左移
if(left - 1 > i && arr[left] === arr[left - 1]){ //去重,与前一个相同时,跳过相同的项,注意边界
left++;
continue;
}
if(right + 1 < len && arr[right] === arr[right + 1]){ //去重,与后一个相同时,跳过相同的项
right--;
continue;
}
if(arr[left] + arr[right] === twoSum){ //相等
result.push([arr[i], arr[left], arr[right]]);
left++;
right--;
}else if(arr[left] + arr[right] < twoSum){
left++;
}else {
right--;
}
}
}
return result;
};
let set = new Set([[1, 2], [1, 2], 2]);
console.log(set);//Set { [ 1, 2 ], [ 1, 2 ], 2 }
console.log(threeSum_2([-4,-2,1,-5,-4,-4,4,-2,0,4,0,-2,3,1,-5,0]));
嗯,超时代码
/**
* @param {number[]} arr
* @return {number[][]}
* 三数之和
* 方法:对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum(三重for嵌套超时),使用双指针
* 难点:结果去重
* 难点解决方法:
1 对结果二维数组去重(超时):数组的项(内层数组)转为字符串,然后一维数组去重,然后再转为二维数组
2 一维数组保存结果(超时),如果数组中有该字符串(每一个结果通过,连接的字符串),最后转为二维数组
*/
var threeSum = function(arr) {
if(arr.length <= 2){
return [];//长度小于等于2
}
arr.sort((a, b) => {
return a - b
});
let len = arr.length;
if(arr[len - 1] < 0 || arr[0] > 0){
return [];
}
let result = [];
for(let i = 0; i < len - 2; i++){
let left = i + 1;
let right = len - 1;
let twoSum = 0 - arr[i];
while(left < right){
if(arr[left] + arr[right] === twoSum){
let str = arr[i] + "," + arr[left] + "," + arr[right];
if(result.indexOf(str) === -1){
result.push(str);
} // 2这个去重也不行
left++;
right--;
}else if(arr[left] + arr[right] < twoSum){
left++;
}else {
right--;
}
}
}
for(let i = 0; i < result.length; i++){
result[i] = result[i].split(",");
for(let j = 0; j < result[i].length; j++){
result[i][j] = Number(result[i][j]);
}
}// 2这个去重也不行
return result;
};
参考:力扣
百里于2020年5月24日
如果有错,请您指出!如有侵权,请联系我删除!