三数之和
三数之和:
给你一个包含 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]
输出:[]
解法
1. 方法1.暴力求解,对于三个数字,循环3次,分别计算和,时间复杂度O(n^3)
2. 方法2.c=-(a+b): 确定了a和b,那就可以想两数之和一样,在map中寻找-(a+b),减少一层循环,时间复杂度O(n^2),空间复杂度O(n)
3. 方法3.排序然后查找
思路:先排序数组,数组长度必须大于3,循环数组,假如当前循环到了i索引,则定义两个指针L = i+1,和R = nums.length-1,如果和sum=nums[i] + nums[L] + nums[R]小于0,则向右移动左指针,如果sum大于0,则左移右指针,如果sum等于0,则正好找到了这3个数,然后在尝试L++,R–,继续寻找中间是否有三个数之和等于0,注意在循环的过程中遇见相同的三个数需要去重。
注:
1 2 3 4 5 6 7 8 9 10
| // 去重(当前数字等于前一个数字则跳出循环) // if (i === 0 || nums[i] !== nums[i - 1]) // 如果不加这个判断则输出为:有重复的数组 // Finished // Your Input // [-1,0,1,2,-1,-4] // Output (64 ms) // [[-1,-1,2],[-1,0,1],[-1,0,1]] // Expected Answer // [[-1,-1,2],[-1,0,1]]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| var threeSum = function (nums) { const result = []; // 对数组进行升序排序 nums.sort(function (a, b) { return a - b; }) for (let i = 0; i < nums.length - 2; i++) { // 去重(当前数字等于前一个数字则跳出循环) if (i === 0 || nums[i] !== nums[i - 1]) { let start = i + 1; end = nums.length - 1; while (start < end) { if (nums[i] + nums[start] + nums[end] === 0) { // push到数组中 result.push([nums[i], nums[start], nums[end]]); start++; end // 去重 while (start < end && nums[start] === nums[start - 1]) { start++; } while (start < end && nums[end] === nums[end + 1]) { end++; } } else if (nums[i] + nums[start] + nums[end] < 0) { start++ } else { end } } } } return result; };
|
复杂度分析:时间复杂度O(n^2),n为数组的长度。空间复杂度O(logn),即排序所需要的空间
最接近的三数之和
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:
输入:nums = [0,0,0], target = 1
输出:0
解法:固定一个数,再双指针(与三数之和解法相似)
思路
- 先将数组从小到大排序,便于微调 sum 的大小。
- 从左到右遍历,先固定一个数,剩下的部分,用头尾双指针扫描
- 如果 sum 大于目标值,就右指针左移,使 sum 变小,否则左指针右移,sum 变大。
- 再看 abs(sum - target) 是否比之前更小了,如果是,将当前 sum 更新给 res
遍历结束,就有了最接近目标值的 sum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| var threeSumClosest = function(nums, target) { nums.sort((a,b)=>a-b) let res =nums[0]+nums[1]+nums[nums.length-1]; for(let i=0;i<nums.length-2;i++){ const n1=nums[i]; let l=i+1; let r =nums.length-1; while(l<r){ const n2 = nums[l] const n3 = nums[r] const sum = n1+n2+n3; if(sum >target){ r-- }else{ l++ } if(Math.abs(sum-target)<Math.abs(res-target)){ res=sum; } } } return res; };
|
Math.abs(x) 函数返回指定数字 “x“ 的绝对值。传入一个非数字形式的字符串或者 undefined/empty 变量,将返回 NaN。传入 null 将返回 0。
1 2 3 4 5 6
| Math.abs('-1'); Math.abs(-2); Math.abs(null); Math.abs("string"); Math.abs();
|
复杂度分析:时间复杂度O(n^2),n为数组的长度。空间复杂度O(logn),即排序所需要的空间
四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
解法
思路同三数之和,注意是双重循环,注意如何去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| var fourSum = function(nums, target) { nums.sort((a,b)=>a-b); const res=[]; // 双层循环,外层循环一次,里面循环执行全部 for(let i=0;i<nums.length-3;i++){ for(let j=i+1;j<nums.length-2;j++){ let low =j+1; let height=nums.length-1; while(low<height){ const sum=nums[i]+nums[j]+nums[low]+nums[height]; if(sum===target){ res.push([nums[i],nums[j],nums[low],nums[height]]) while(nums[low]===nums[low+1] ) low++; while(nums[height]===nums[height-1]) height--; low++; height--; }else if(sum<target){ low++ }else{ height--; } } while(nums[j] ===nums[j+1] ) j++; } while(nums[i]===nums[i+1]) i++; } return res; };
|
参考文章
LeetCode-Solution–三数之和