LeetCode1. 两数之和、15. 三数之和、18 四数之和

102 阅读1分钟

一、两数之和

leetcode 1.两数之和

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 ] ]

二、三数之和

LeetCode 15.三数之和

现在我们想找和为 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
}

三、四数之和

leetcode 18.四数之和

官方解法,直接两层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)
};

参考文章

mp.weixin.qq.com/s/fSyJVvggx…