题目
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
解析
这道题不适合使用哈希表,因为要去除重复解,可以用双指针来解;
1.先判断边界条件,若数值小于3直接return空数组;
2.然后对数组进行排序,排序后遍历数组,对里面的数据再进行处理;
3.如果第一个数大于0,则再加上后面的两个(排序了),必然不会等于0;
4.如果当前数字等于上一个数字,也跳过(重复)
这道题属实是没写出来,写了三遍也没记住
func threeSum(nums []int) [][]int {
res := [][]int{}
n := len(nums)
if n < 3 {
return res
}
sort.Ints(nums)
for i := 0; i < n-2; i++ {
// 几个异常条件
// 第一个就大于0,已经排序了,后面的都会大于0
if nums[i] > 0 {
break
}
// 去重,题目中要求了不能有重复的组合
if i > 0 && nums[i] == nums[i-1] {
continue
}
// 前三个加起来大于0了,后面的也会大于0
if nums[i]+nums[i+1]+nums[i+2] > 0 {
break
}
// 第一个数和最后的俩数加起来还小于0,直接结束这次的循环,让第一个数加点,可能还有救
if nums[i]+nums[n-1]+nums[n-2] < 0 {
continue
}
// 前面的都可以后,在这里采用左右双指针
j := i + 1
k := n - 1
for j < k {
sum := nums[i] + nums[j] + nums[k]
if sum > 0 {
k--
} else if sum < 0 {
j++
} else {
// 符合预期,先追加进结果
res = append(res, []int{nums[i], nums[j], nums[k]})
// 再分别对jk去重
j++
for j < k && nums[j] == nums[j-1] {
j++
}
k--
for j < k && nums[k] == nums[k+1] {
k--
}
}
}
}
return res
}
167 两数之和II
给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
解析
这道题因为已经给定了是有序的,然后就可以使用双指针来进行遍历:
时间复杂度是O(n)
空间复杂度是O(1)
func twoSum(numbers []int, target int) []int {
res := []int{}
left := 0
right := len(numbers) - 1
for left < right {
if numbers[left] + numbers[right] == target {
return []int{left+1, right+1}
} else if numbers[left] + numbers[right] < target {
left++
} else {
right--
}
}
return res
}
16 最接近的三数之和
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
解析
和上面的三数之和的基本解题思路是一致的,也是处理双指针,大部分的逻辑都相同,仅仅在处理目标结果的时候有所差异
func threeSumClosest(nums []int, target int) int {
n := len(nums)
sort.Ints(nums)
res := 0
minDiff := math.MaxInt
for i := 0; i < n-2; i++ {
// 去重
if i > 0 && nums[i] == nums[i-1] {
continue
}
// 前三个大于目标值了,后面的只会更大,直接就可以结束了
sum := nums[i] + nums[i+1] + nums[i+2]
if sum > target {
if sum-target < minDiff {
res = sum
}
break
}
// 第一个和最后的两个还小于目标值,还有救
sum = nums[i] + nums[n-2] + nums[n-1]
if sum < target {
if target-sum < minDiff {
minDiff = target - sum
res = sum
}
continue
}
// 这里才开始双指针
j := i + 1
k := n - 1
for j < k {
sum = nums[i] + nums[j] + nums[k]
if sum == target {
return target
} else if sum > target {
if sum-target < minDiff {
minDiff = sum - target
res = sum
}
k--
} else {
if target-sum < minDiff {
minDiff = target - sum
res = sum
}
j++
}
}
}
return res
}
18 四数之和
题目和三数之和基本相同,就是最后要等于target,不是0了
func fourSum(nums []int, target int) [][]int {
ans := [][]int{}
n := len(nums)
if n < 4 {
return ans
}
sort.Ints(nums)
for a := 0; a < n-3; a++ { // 枚举第一个数字
x := nums[a]
if a > 0 && x == nums[a-1] { // 跳过重复数字
continue
}
if x+nums[a+1]+nums[a+2]+nums[a+3] > target {
break
}
if x+nums[n-1]+nums[n-2]+nums[n-3] < target {
continue
}
for b := a + 1; b < n-2; b++ {
y := nums[b]
if b > a+1 && y == nums[b-1] {
continue
}
if x+y+nums[b+1]+nums[b+2] > target {
break
}
if x+y+nums[n-1]+nums[n-2] < target {
continue
}
c := b + 1
d := n - 1
for c < d {
sum := x + y + nums[c] + nums[d]
if sum > target {
d--
} else if sum < target {
c++
} else {
ans = append(ans, []int{x, y, nums[c], nums[d]})
c++
for c < d && nums[c] == nums[c-1] {
c++
}
d--
for c < d && nums[d] == nums[d+1] {
d--
}
}
}
}
}
return ans
}