算法:两数之和&三数之和

279 阅读1分钟

1. 两数之和

给定一个整数数组 nums 和一个目标值 target ,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例 1:

输入:nums = [2,7,11,15], target = 9

输出:[0,1]

解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6 输出:[1,2]

解题思路:

  • 初始化一个 map = new Map()
  • 从第一个元素开始遍历 nums
  • 获取目标值与 nums[i] 的差值,即 j = target - nums[i] ,判断差值在 map 中是否存在
  • 不存在( map.has(j) 为 false ) ,则将 nums[i] 加入到 map 中(key为nums[i], value为 i ,方便查找map中是否存在某值,并可以通过 get 方法直接拿到下标)
  • 存在( map.has(j) ),返回 [map.get(j), i] ,求解结束
  • 遍历结束,则 nums 中没有符合条件的两个数,返回 []

时间复杂度:O(n)

const twoSum = function(nums, target) {
    let map = new Map()
    for(let i = 0; i < nums.length; i++) {
        let j = target-nums[i]
        if(map.has(j)) {
            return [map.get(j), i]
        }
        map.set(nums[i], i)
    }
    return []
}

2.三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c  ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意:  答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]

输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []

输出:[]

解法1:

我们可以继续按照两数之和的思路解题,遍历数组,选定一个数( nums[i] )作为三数之和的第一个数,然后题目就换成了在 i+1 到 nums.length-1 中两数之和问题。 但会出现一个问题:结果中可能会出现重复的三元组,需要去重

代码实现

function threeSum(nums) {
    let set
    let result = []
    // 排序 目的是去重
    nums.sort((a, b) => (a - b))
    for(let i = 0; i < nums.length - 2; i++) {
      set = new Set()
      while (nums[i] === nums[i - 1]) i++ // 去重
      // 第一个数
      let first = nums[i]
      let j = i + 1
      while (j < nums.length) {
        // 第三个数
        let second = 0 - nums[j] - first
        let third = nums[j]
        if(set.has(second)) {
          result.push([first, second, third])
          set.add(third)
          j++
          while (nums[j] === nums[j - 1]) j++  // 去重
        } else {
          set.add(third)
          j++
        }
      }
    }
    return result
}

解法2: 排序 + 双指针

解题思路:  

数组先排序,排序完后遍历数组,以 nums[i] 作为第一个数 first ,以 nums[i+1] 作为第二个数 second ,将 nums[nums.length - 1] 作为第三个数 last ,判断三数之和是否为 0 ,

  • <0 ,则 second 往后移动一位(nums 是增序排列),继续判断
  • >0 ,则 last 往前移动一位(nums 是增序排列),继续判断
  • ===0 ,则进入结果数组中,并且 second 往后移动一位, last 往前移动一位,继续判断下一个元组

直至 second >= last 结束循环,此时, nums[i] 作为第一个数的所有满足条件的元组都已写入结果数组中了,继续遍历数组,直至 i === nums.length - 2 (后面需要有 second 、 last )

代码实现:

function threeSum(nums) {
  if(!nums || nums.length < 3) return []
  let result = [], second, last
  // 排序
  nums.sort((a, b) => a - b) 
  for (let i = 0; i < nums.length - 2 ; i++) {
      if(nums[i] > 0) break
      // 去重
      if(i > 0 && nums[i] === nums[i-1]) continue
      second = i + 1
      last = nums.length - 1
      while(second < last){
          const sum = nums[i] + nums[second] + nums[last]
          if(!sum){
              // sum 为 0
              result.push([nums[i], nums[second], nums[last]])
              // 去重
              while (second < last && nums[second] === nums[second+1]) second++ 
              while (second < last && nums[last] === nums[last-1]) last--
              second++
              last--
          }
          sum < 0 && second ++
          sum > 0 && last --
      }
  }        
  return result
}