相向双指针 leetcode 15 三数之和 + 167两数之和II + 16最接近的三数之和 + 18四数之和

题目

给你一个整数数组 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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值