「leetcode」1.两数之和;15.三数之和;16. 最接近的三数之和;18. 四数之和

697 阅读5分钟

1.两数之和

原题

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

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

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路

本题主要可以使用三种思路解决,暴力法,hash法,双指针法。

  1. 暴力法主要运用双层for循环,简单,暴力,这里不在赘述。
  2. hash法的思路,利用hash查询时复杂度为O(1), 将暴力法的O(n^2)时间复杂度优化为O(n)。使用hash法时,需要注意不要重复的使用,同一个数字。
  3. 双指针法,需要先对数组进行从小到大的排序,第一个指针指向数组的头部,第二个指针指向数组的尾部。如果两个指针对应两个的数字的和小于target,那么将头部的指针向前移动一位。如果两个数字的和大于target,那么将尾部的指针向后移动一位。

代码

代码1: hash法


/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    const hash = {}
    
    for (let i = 0; i < nums.length; i++) {
        // 使用数组,避免重复的数字
        if (!hash[nums[i]]) {
            hash[nums[i]] = [i]
        } else {
            hash[nums[i]].push(i)
        }
    }
    
    for (let i = 0; i < nums.length; i++) {
        if (hash[target - nums[i]]) {
            for (let j = 0; j < hash[target - nums[i]].length; j++) {
                if (hash[target - nums[i]][j] !== i) {
                    return [i, hash[target - nums[i]][j]]
                }
            }
        }
    }
        
    return []
};

代码2: 双指针法


/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let start = 0
    let end = nums.length - 1
    
    nums = nums.map((v, i) => { return { v, i } })
    
    nums = nums.sort((a, b) => a.v - b.v)
    
    while (start < end) {
        if (nums[start].v + nums[end].v > target) {
            end -= 1
        } else if (nums[start].v + nums[end].v < target) {
            start += 1
        } else {
            return [nums[start].i, nums[end].i]
        }
    }
        
    return []
};

15.三数之和

原题

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

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

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

思路

15, 16, 18题的思路,是基本类似的。总体的思路都是利用数组头部尾部的双指针逼近target,对于结果使用hash进行去重

首先需要将数组,进行从小到大的排序。从nums[i]开始,第一个指针headIndex指向nums[i+1], 第二个指针tailIndex指向nums[nums.length - 1]

如果三数的和大于target,我们将tailIndex向前移动一位。如果三数的和小于target,我们将headIndex向后移动一位。直到找到和等于target的三个数。

由于题目中要求不能出现重复的结果。比如当target = 0时,虽然[-1, 0, 1][-1, 1, 0]都满足结果,但是属于重复的结果。

我们将得到的三元组,使用join连接成一个key,将key对应的value设置为true,每一次push进结果数组前,都使用hash检查,检查是否存在相同的key,实现结果数组的去重。

代码


/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    
    if (nums.length < 3) {
        return []
    }
    
    const result = []
    
    let hash = {}    
    let hi = -1
    let ti = -1
    
    nums = nums.sort((a, b) => a - b)
    
    for (let i = 0; i < nums.length - 2; i++) {
        hi = i + 1
        ti = nums.length - 1
        if (nums[i] === nums[i - 1]) {
            continue
        }
        const target = 0 - nums[i]
        while (hi < ti) {
            if (nums[hi] + nums[ti] > target) {
                ti -= 1
            } else if (nums[hi] + nums[ti] < target) {
                hi += 1
            } else {
                const k = [nums[i], nums[hi], nums[ti]].join('')
                if (!hash[k]) {
                    hash[k] = true
                    result.push([nums[i], nums[hi], nums[ti]])
                }
                ti -= 1
                hi += 1
            }
        }
    }
    
    return result
};

16. 最接近的三数之和

原题

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.

与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

思路

本题的思路,基本同15题类似。在每一次循环中,都会对三数的和与target进行比较。保留三数和与target的差值中的绝对值最小的哪一个的和。

代码


/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var threeSumClosest = function(nums, target) {
  let hi = -1
  let ti = -1
  let diff = Number.MAX_VALUE 
  let sum = 0
  
  nums = nums.sort((a, b) => a - b)
  
  for (let i = 0; i < nums.length - 2; i++) {
      
      if (nums[i] === nums[i - 1]) {
          continue
      }
      
      let hi = i + 1
      
      let ti = nums.length - 1
      
      while (hi < ti) {
          
          let temp = 0
          
          if (nums[hi] + nums[ti] > target - nums[i]) {
              temp = nums[hi] + nums[ti] + nums[i]
              ti -= 1
          } else if (nums[hi] + nums[ti] < target - nums[i]) {
              temp = nums[hi] + nums[ti] + nums[i]
              hi += 1
          } else {
              // 直接返回结果即可
              sum = target
              return sum
          }
          // 比较绝对
          if (Math.abs(target - temp) < diff) {
              diff = Math.abs(target - temp)
              sum = temp
          }
      }
  }
      
  return sum
  
};

18. 四数之和

原题

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:

答案中不可以包含重复的四元组。

示例:

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

思路

思路依然和15题类似,我们只需要将四数问题,拆解为三数问题。只需要在三数之和基础上,增加一层for循环即可。

代码


/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    if (nums.length < 4) {
        return []
    }
    // 拆解成三数之和的问题,多一层循环
    
    const result = []
    const hash = {}
    let hi = -1
    let ti = -1
    
    nums = nums.sort((a, b) => a - b)
    
    for1: for (let i = 0; i < nums.length - 3; i++) {
        if (nums[i] === nums[i - 1]) {
            continue for1
        }
        let tw = target - nums[i]
        for2: for (let j = i + 1; j < nums.length - 2; j++) {
            let tv = tw - nums[j]
            hi = j + 1
            ti = nums.length - 1
            while (hi < ti) {
                if (nums[hi] + nums[ti] > tv) {
                    ti -= 1
                } else if (nums[hi] + nums[ti] < tv) {
                    hi += 1
                } else {
                    const k = [nums[i], nums[j], nums[hi], nums[ti]].join('')
                    if (!hash[k]) {
                        hash[k] = true
                        result.push([nums[i], nums[j], nums[hi], nums[ti]])
                    }
                    ti -= 1
                    hi += 1
                }
            }
        }
    }
    
    return result
};