1.两数之和
原题
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路
本题主要可以使用三种思路解决,暴力法,hash法,双指针法。
- 暴力法主要运用双层
for循环,简单,暴力,这里不在赘述。 - hash法的思路,利用hash查询时复杂度为
O(1), 将暴力法的O(n^2)时间复杂度优化为O(n)。使用hash法时,需要注意不要重复的使用,同一个数字。 - 双指针法,需要先对数组进行从小到大的排序,第一个指针指向数组的头部,第二个指针指向数组的尾部。如果两个指针对应两个的数字的和小于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
};