让我们一起啃算法----三数之和
三数之和(3 Sum)
题干:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源:力扣
思路与 让我们一起啃算法—-两数之和 相似,求 两数之和 时我们引入了一个偏移指针,这题需要引入 三个 偏移指针。
解题思路
首先我们对 nums 进行排序,接着初始化: a 指向 nums 数组的第一个元素,b 指向 nums[a] 的后一个元素,c 指向 nums 的最后一个元素。
初始化时,b 始终指向 nums[a] 的后一个元素, c 始终指向 nums 的最后一个元素。
题目要求三数之和为 0,即 nums[b] + nums[c] = 0 - nums[a],我们将 0 - nums[a] 记为 sum。
当 nums[b] + nums[c] 大于 sum 时,c 向左移动;当 nums[b] + nums[c] 小于 sum 时,b 向右移动;当 nums[b] + nums[c] 等于 sum 时,这时候 nums[a], nums[b], nums[c] 为一组有效值,记录下来,b 向右移动,c 向左移动。直到 b 大于等于 c 时,移动 a 指针再重新初始化 b 和 c,重复上面的步骤,直到 a 越界。
a 越界条件: a 大于 nums 数组倒数第三个索引时即越界。因为我们有三个指针,当 a 为数组倒数第三个索引时,b为倒数第二个索引,c为倒数第一个索引。
这里有一个注意点: a,b,c 指针在移动的过程中需要与前一次指向的数组值对比,如果相等需要继续移动,直到不相等。例如 nums[b] 为 1,b 向右移动一位,即 b++, 这时候发现 nums[b] 仍为 1,b 需要继续向右移动一位,直到 nums[b] 不为 1。
上面的操作是为了保证结果中没有重复的元组。
例如 nums 为 [-1 -1 0 1 2],假设 a 为 0,即 nums[a] 为 -1,会找到一个有效元组 [-1, 0, 1],这时候向右移动 a ,即 a 为 1,nums[a] 为 -1 与上一次值相同,a 需要继续向右移动,否则又会找到一个有效元组 [-1, 0, 1],这就重复了。同理 b,c 也是如此。
流程图如下:
代码实现
GO语言实现
func threeSum(nums []int) [][]int {
var result [][]int
if len(nums) <= 2 {
return result
}
// 需要排序
sort.Ints(nums)
a := 0
// 有三个指针,a 是最左边的,所以 a 最大值为 nums 倒数第三个索引
for a <= len(nums)- 3 {
// 保证 a 在向右移动时指向的值,不与之前的值相等
if a != 0 && nums[a] == nums[a-1] {
a++
continue
}
// b 为 a 的后一个指针
b := a + 1
// c 指向 nums 最后一个元素
c := len(nums) -1
// sum 的值为后续 nums[b] + nums[c] 的值
sum := 0 - nums[a]
// b 必须小于 c
for b < c {
// 保证 c 在向左偏移时指向的值,不与之前的相等
if c < len(nums) - 1 && nums[c] == nums[c + 1] {
c--
continue
}
// 保证 b 在向右偏移时指向的值,不与之前的相等
if b > a + 1 && nums[b] == nums[b-1] {
b++
continue
}
// 比较和
if nums[b]+nums[c] > sum {
c--
} else if nums[b]+nums[c] < sum {
b++
} else {
result = append(result, []int{nums[a], nums[b], nums[c]})
c--
b++
}
}
// a 向右移动
a++
}
return result
}
总结
每天进步一点点,加油!
算法教程项目,每天更新一题,点个 star 支持一下呀:
github.com/wx-satellite/learning-a...
本作品采用《CC 协议》,转载必须注明作者和本文链接
优秀!