【LeetCode】15. 三数之和
问题分析
题目要求找出数组中所有满足条件的三元组 [nums[i], nums[j], nums[k]],使得三数之和为 0,且结果不重复。核心难点在于 高效去重 和 优化时间复杂度。以下是基于搜索结果的 Golang 实现方案:
实现思路
1. 排序与双指针法
- 排序:对数组排序后,便于跳过重复元素和调整指针方向。
- 双指针策略:
- 固定第一个数
nums[i],然后用左右指针left和right在剩余区间搜索两数之和为-nums[i]的组合。 - 若三数之和
>0,则右指针左移;若<0,则左指针右移;若=0,记录结果并去重。
- 固定第一个数
- 时间复杂度:
O(n^2),优于暴力三重循环的O(n^3)。
2. 去重逻辑
- 外层循环去重:当
nums[i] == nums[i-1]时跳过,避免重复固定相同的第一个数。 - 内层双指针去重:找到有效三元组后,跳过所有与
nums[left]和nums[right]相同的相邻元素。
3. 剪枝优化
- 提前终止:若
nums[i] > 0,由于数组已排序,后续无法找到三数和为0的组合,直接终止循环。
Golang 实现代码
import "sort" func threeSum(nums []int) [][]int { sort.Ints(nums) // 先排序 result := [][]int{} n := len(nums) for i := 0; i < n-2; i++ { // 跳过重复的第一个数 if i > 0 && nums[i] == nums[i-1] { continue } // 剪枝:若第一个数已大于0,后续无解 if nums[i] > 0 { break } left, right := i+1, n-1 target := -nums[i] for left < right { sum := nums[left] + nums[right] if sum == target { result = append(result, []int{nums[i], nums[left], nums[right]}) // 跳过重复的左右指针元素 for left < right && nums[left] == nums[left+1] { left++ } for left < right && nums[right] == nums[right-1] { right-- } left++ right-- } else if sum < target { left++ } else { right-- } } } return result }
关键点说明
-
排序的作用:
- 确保相同值的元素相邻,便于去重。
- 双指针的移动方向可预测,便于调整搜索范围。
-
去重逻辑的细节:
- 外层循环去重条件为
i > 0 && nums[i] == nums[i-1],而非i < n-2 && nums[i] == nums[i+1],避免遗漏有效组合。 - 内层循环去重需在记录结果后立即处理,防止重复添加相同三元组。
- 外层循环去重条件为
-
性能优化:
- 排序后提前终止循环(
nums[i] > 0)减少无效计算。 - 双指针法将时间复杂度优化至
O(n^2)。
- 排序后提前终止循环(
测试示例验证
- 示例1:输入
[-1,0,1,2,-1,-4],输出[[-1,-1,2],[-1,0,1]]。 - 示例2:输入
[0,1,1],输出[]。 - 示例3:输入
[0,0,0],输出[[0,0,0]]。
复杂度分析
| 维度 | 说明 |
|---|---|
| 时间复杂度 | O(n^2)(排序 O(nlogn) + 双指针 O(n^2)) |
| 空间复杂度 | O(1)(忽略排序的栈空间) |
扩展与优化
- 四数之和:可参考双指针法的扩展,固定前两个数,再用双指针搜索后两数。
- 哈希表法:虽可实现但需额外空间存储中间结果,且去重逻辑复杂,推荐优先使用双指针。
如需进一步优化内存,可预分配 result 切片容量。

浙公网安备 33010602011771号