一、两数之和
function twoSum(nums: number[], target: number): number[] {
const twoSumMap = new Map()
for (let i = 0; i < nums.length; i++) {
const other = target - nums[i]
if (twoSumMap.has(other)) {
return [twoSumMap.get(other), i]
} else {
twoSumMap.set(nums[i], i)
}
}
return []
}
对于 TwoSum 问题,需要注意到给定的数组是无序。一般情况下,我们会首先把数组排序再考虑双指针技巧。而TwoSum 启发我们,HashMap也可以帮助我们处理无序数组相关的简单问题。
如果 TwoSum I 中给的数组是有序的,可以利用双指针方式。下面升级难度,假设上述答案存在多组,需要返回不重复的答案数组。采用排序双指针的方式来解决这个问题
function twoSum(nums, target){
// 排序
nums.sort((l,r)=> l-r)
// 双指针
let left = 0, right = nums.length -1
const resArr = []
while(left < right){
const leftNums = nums[left]
const rightNums = nums[right]
const sum = leftNums + rightNums
if(sum === target){
resArr.push([leftNums, rightNums])
// 跳过重复的数字
while (left < right && nums[left] === leftNums) {
left++;
}
while (left < right && nums[right] === rightNums) {
right--;
}
}else if(sum < target){
left ++
}else if(sum > target){
right --
}
}
return resArr
}
// 测试
console.log(twoSum([1, 0, -1, 0, -2, 2], 0))
// [ [ -2, 2 ], [ -1, 1 ], [ 0, 0 ] ]
二、三数之和
现在我们想找和为 target
的三个数字,第一个数字可以nums
中的任意一个数字,而剩下两个数字则转化为两数之和问题:找到和为 target - nums[i]
的两个数字。
不重复的问题:关键点在于,不能让第一个数重复,至于后面的两个数,我们复用的 twoSum
函数会保证它们不重复。所以代码中必须用一个 while 循环来保证 3Sum
中第一个元素不重复。
function threeSum(nums: number[]): number[][] {
const len = nums.length
if(len<3) {
return []
}
// 排序
nums.sort((l,r)=> l-r)
const res = []
for(let i = 0; i<len-2; i++){
const item = nums[i]
const target = 0 - item
const resTwo = twoSum(nums, i+1, target)
if(resTwo.length > 0){
const triELe = resTwo.map(ele => [item, ...ele])
res.push(...triELe)
}
while(i < len-2 && item === nums[i+1]){
i++
}
}
return res
};
function twoSum(nums: number[], start: number, target: number): number[][]{
let lo = start;
let hi = nums.length-1;
if(nums.length - 1 - start < 1){
return []
}
const res = []
while(lo < hi){
const left = nums[lo];
const right = nums[hi];
const sum = left + right;
if(sum === target){
res.push([left, right])
while(lo < hi && left === nums[lo]){
lo ++;
}
while(lo < hi && right === nums[hi]){
hi --;
}
}else if(sum < target){
while(lo < hi && left === nums[lo]){
lo ++;
}
}else{
while(lo < hi && right === nums[hi]){
hi --;
}
}
}
return res
}
三、四数之和
官方解法,直接两层for循环解决第一和第二个数字,然后再内层while循环找两数之和
var fourSum = function(nums, target) {
const quadruplets = [];
if (nums.length < 4) {
return quadruplets;
}
nums.sort((x, y) => x - y);
const length = nums.length;
for (let i = 0; i < length - 3; i++) {
if (i > 0 && nums[i] === nums[i - 1]) {
continue;
}
if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if (nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
for (let j = i + 1; j < length - 2; j++) {
if (j > i + 1 && nums[j] === nums[j - 1]) {
continue;
}
if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}
if (nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
let left = j + 1, right = length - 1;
while (left < right) {
const sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum === target) {
quadruplets.push([nums[i], nums[j], nums[left], nums[right]]);
while (left < right && nums[left] === nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] === nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return quadruplets;
};
四、nSum解决四数之和
为了使用以后的n数之和,提取可复用nSum方法
function nSum(nums, n, start, target) {
const res = []
if(n<2 || nums.length<n){
return []
}
if(n === 2){
let lo = start;
let hi = nums.length -1;
while(lo<hi){
const left = nums[lo]
const right = nums[hi]
const sum = left + right
if (sum === target ){
res.push([left, right])
while(lo < hi && nums[lo] === left){
lo++
# }
while (lo < hi && nums[hi] === right) {
hi--
}
}else if(sum<target){
while (lo < hi && nums[lo] === left) {
lo++
}
}else{
while (lo < hi && nums[hi] === right) {
hi--
}
}
}
}else{
for(let i= start; i< nums.length -1 ; i++){
const item = nums[i]
const resMiddle = nSum(nums, n-1, i+1, target- item)
if (resMiddle.length>0){
const resEleArr = resMiddle.map(ele=> [item, ...ele])
res.push(...resEleArr)
}
// 注意这里条件是,item === nums[i+1]。外层for循环会加1,所以这里默认不执行,只有和下一个重复时,才再执行一次。
while (i < nums.length - 2 && item === nums[i+1]) {
i++
}
}
}
return res
}
function fourSum(nums: number[], target: number): number[][] {
nums.sort((l, r) => l - r)
return nSum(nums, 4, 0, target)
};