20200612
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
答案中不可以包含重复的三元组
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
先排序
特殊情况排除
第一次循环得到的结果作为第一个数,当第一个数
当第一个数大于 0,则说明之后不会有与他组合满足条件的数了
第二个数从第一个之后依次向后查找
第三个数从最后一个数依次向前查找
当第二个数的指针大于最后一个数的指针时终止循环
当数组总存在重复的数组时,按照上面的逻辑会有重复的答案出现
初始化一个 map 去满足条件的任何两个数组合作为 map 的 key 来
当判断筛选出来的两个数组合的 key 在 map 中出现过则说明该组合已经放到结果数组中了
a+b => b+a a+b
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function (nums) {
let end = nums.length,
_result = []
nums.sort((a, b) => a - b)
if (nums.length < 3 || nums[end - 1] < 0) return []
let secondMap = new Map()
nums.some((first, i) => {
if (first > 0) return true
let second = i + 1
let last = end - 1
while (second < last) {
if (first + nums[second] > 0) break
let sum = first + nums[second] + nums[last]
if (
sum === 0 &&
!(
secondMap.has(nums[second] + '-' + nums[last]) ||
secondMap.has(nums[last] + '-' + nums[second])
)
) {
_result.push([first, nums[second], nums[last]])
secondMap.set(nums[second] + '-' + nums[last], second)
}
if (sum <= 0) {
second++
} else {
last--
}
}
})
return _result
}
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function (nums) {
let sortArr = [],
_result = [],
n = nums.length
nums.sort((a, b) => a - b)
// 枚举 a
for (let first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue
}
// c 对应的指针初始指向数组的最右端
let third = n - 1
let target = -nums[first]
// 枚举 b
for (let second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b
if (second == third) {
break
}
if (nums[second] + nums[third] == target) {
_result.push([nums[first], nums[second], nums[third]])
}
}
}
return _result
}
const res = []
let numsLen = nums.length
if (numsLen < 3) return []
nums.sort((a, b) => a - b)
let lastNum = null
for (let i = 0; i < numsLen; i++) {
if (nums[i] > 0) break
if (nums[i] === lastNum) continue
let L = i + 1
let R = numsLen - 1
while (L < R) {
const sum = nums[i] + nums[L] + nums[R]
if (sum === 0) {
res.push([nums[i], nums[L], nums[R]])
while (nums[L] === nums[L + 1]) L++
while (nums[R] === nums[R - 1]) R--
L++
R--
} else if (sum > 0) {
R--
} else {
L++
}
}
lastNum = nums[i]
}
return res
双指针的移动时,避免出现重复解
得到一个解后,需要左右指针向“内”移动,为了避免指向重复的元素
左指针要在 left < right 的前提下,一直向右移动到不重复的元素上
右指针要在 left < right 的前提下,一直向左移动到不重复的元素上
经过了排序,如果外层遍历的数已经大于 0,则另外两个数一定大于 0,sum 不会等于 0,所以这种情况直接 break 结束遍历
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function (nums) {
nums.sort((a, b) => a - b)
const res = []
for (let i = 0; i < nums.length - 2; i++) {
let n1 = nums[i]
if (n1 > 0) break
if (n1 == nums[i - 1] && i > 0) continue // 遍历到重复的数,跳过
let left = i + 1
let right = nums.length - 1
while (left < right) {
let n2 = nums[left]
let n3 = nums[right]
if (n1 + n2 + n3 === 0) {
res.push([n1, n2, n3])
while (left < right && nums[left] === n2) left++ //直到指向不一样的数
while (left < right && nums[right] === n3) right--
} else if (n1 + n2 + n3 < 0) {
left++
} else {
right--
}
}
}
return res
}
看了别人的方法就会发现,循环的逻辑基本差不多的,
首先找到一个基准
再对有序的数组,有节制的循环
不同的地方(也是我可以优化的地方)主要是对有序查询第二个和第三个数据重复的问题
声明 map 记录重复不仅增加了内存的占用,也在 map 存储和查询是增加了时间成本
优化:
第二个数后面一个数增加后与上一个相同则默认重复
最后一个数也类似
博客: 小书童博客(http://gaowenju.com/)