当前位置: 首页 > news >正文

【LeetCode】15. 三数之和

文章目录

  • 15. 三数之和
    • 题目描述
    • 示例 1:
    • 示例 2:
    • 示例 3:
    • 提示:
    • 解题思路
      • 方法一:排序 + 双指针(推荐)
      • 方法二:排序 + 哈希辅助查找(备选)
    • 代码实现(Go)
    • 复杂度分析
    • 测试用例
    • 边界与注意点
    • 运行
    • 完整题解代码

15. 三数之和

题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入: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] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -10^5 <= nums[i] <= 10^5

解题思路

方法一:排序 + 双指针(推荐)

核心思想:

  • 先对数组从小到大排序;
  • 枚举第一个数 i,随后在区间 [i+1, n-1] 用双指针 l、r 寻找两数之和为 -nums[i];
  • 为避免重复:
    • i 若与前一个数相同则跳过;
    • 每次找到一个解后,移动 l 与 r 并跳过与上一位置相同的值。

步骤:

  1. 排序 nums;
  2. 枚举 i in [0…n-3]:若 nums[i] > 0,直接 break;若 i>0 且 nums[i]==nums[i-1],continue;
  3. 令 l=i+1, r=n-1,计算 s=nums[i]+nums[l]+nums[r]:
    • s==0:记录三元组并跳过重复的 l、r;
    • s<0:l++;
    • s>0:r–。

复杂度:

  • 时间 O(n^2),空间 O(1) 额外空间(不计返回结果)。

方法二:排序 + 哈希辅助查找(备选)

思路:

  • 固定 i 后,在右侧用哈希集合寻找 two-sum 的补数 target=-nums[i];
  • 小心控制去重。实现上不如双指针直观,一般作为备选或对拍。

时间复杂度同样为 O(n^2)。

代码实现(Go)

// 方法一:排序 + 双指针
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++ {if i > 0 && nums[i] == nums[i-1] { // 去重continue}if nums[i] > 0 { // 剪枝break}l, r := i+1, n-1for l < r {s := nums[i] + nums[l] + nums[r]if s == 0 {res = append(res, []int{nums[i], nums[l], nums[r]})l++for l < r && nums[l] == nums[l-1] { l++ }r--for l < r && nums[r] == nums[r+1] { r-- }} else if s < 0 {l++} else {r--}}}return res
}

复杂度分析

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)(不计输出结果)

测试用例

func main() {cases := [][]int{{-1, 0, 1, 2, -1, -4}, // [[-1,-1,2],[-1,0,1]]{0, 1, 1},             // []{0, 0, 0},             // [[0,0,0]]}for _, c := range cases {fmt.Println(threeSum(c))}
}

边界与注意点

  • 多个零:如 [0,0,0,0] 结果应只有 [0,0,0]
  • 大量重复值时的去重处理
  • 排序后才能使用双指针并简化去重逻辑

运行

在当前目录下运行:

go run main.go

完整题解代码

package mainimport ("fmt""sort"
)// 三数之和 - 排序 + 双指针(推荐)
// 时间复杂度:O(n^2),空间复杂度:O(1) 额外空间(不计结果集)
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++ {// 去重:固定数与前一个相同则跳过if i > 0 && nums[i] == nums[i-1] {continue}// 剪枝:若最小的固定数已大于0,则不可能再找到和为0if nums[i] > 0 {break}l, r := i+1, n-1for l < r {s := nums[i] + nums[l] + nums[r]if s == 0 {res = append(res, []int{nums[i], nums[l], nums[r]})// 移动指针并跳过重复值l++for l < r && nums[l] == nums[l-1] {l++}r--for l < r && nums[r] == nums[r+1] {r--}} else if s < 0 {l++} else {r--}}}return res
}// 备用思路:固定一个数 + 哈希集合查找 two-sum 的补数,同时控制去重
// 为演示而保留,复杂度同样 O(n^2),实现上不如双指针直观
func threeSumWithHash(nums []int) [][]int {res := [][]int{}n := len(nums)if n < 3 {return res}sort.Ints(nums)for i := 0; i < n-2; i++ {if i > 0 && nums[i] == nums[i-1] {continue}target := -nums[i]seen := make(map[int]bool)// 为防重:记录已经加入结果的第二个数usedSecond := make(map[int]bool)for j := i + 1; j < n; j++ {need := target - nums[j]if seen[need] {// 三元组为 nums[i], need, nums[j],此时 need <= nums[j]if !usedSecond[need] { // 控制去重res = append(res, []int{nums[i], need, nums[j]})usedSecond[need] = true}}seen[nums[j]] = true}}return res
}// 简易校验:判断所有三元组和为 0,且三元组内部为非降序,整体无重复
func validateTriplets(ans [][]int) bool {seen := make(map[[3]int]bool)for _, t := range ans {if len(t) != 3 {return false}a, b, c := t[0], t[1], t[2]if a+b+c != 0 {return false}if !(a <= b && b <= c) {return false}key := [3]int{a, b, c}if seen[key] {return false}seen[key] = true}return true
}func main() {fmt.Println("=== 15. 三数之和 ===")cases := []struct {nums []intname string}{{[]int{-1, 0, 1, 2, -1, -4}, "示例1"},{[]int{0, 1, 1}, "示例2"},{[]int{0, 0, 0}, "示例3"},{[]int{0, 0, 0, 0}, "多个零"},{[]int{-2, 0, 1, 1, 2}, "包含重复与正负"},{[]int{3, -2, 1, 0}, "无解"},}for _, c := range cases {fmt.Printf("%s: %v\n", c.name, c.nums)ans := threeSum(append([]int(nil), c.nums...))fmt.Printf("双指针解法结果: %v\n", ans)fmt.Printf("结果校验: %v\n\n", validateTriplets(ans))}// 可选:对比哈希解法输出(仅作为参考)extra := []int{-1, 0, 1, 2, -1, -4}fmt.Printf("对比(哈希解法): %v\n", threeSumWithHash(append([]int(nil), extra...)))
}
http://www.dtcms.com/a/337544.html

相关文章:

  • 从财务整合到患者管理:德国医疗集团 Asklepios完成 SAP S/4HANA 全链条升级路径
  • 贪心算法(Greedy Algorithm)详解
  • 【机器学习】Macro-F1(宏平均 F1)是什么?
  • SWMM排水管网水力、水质建模及在海绵与水环境中的应用技术-模拟降雨和污染物质经过地面、排水管网、蓄水和处理
  • Jenkins启动端口修改失败查找日志
  • 音频算法工程师技能1
  • Vue2篇——第五章 Vue.js 自定义指令与插槽核心
  • 【序列晋升】:9 Service Mesh微服务通信的基础设施革命
  • 电子元器件-电容终篇:基本原理、参数解读、电路作用、分类及区别、应用场景、选型、降频及实战案例
  • Linux 系统~存储高级技术
  • C++ 中的 delete 与 default 关键字详解
  • diffusion model(1.4) 相关论文阅读清单
  • 遥感数字图像处理教程——第三章课后习题
  • flutter项目适配鸿蒙
  • 人工智能 | 基于大数据的皮肤病症状数据可视化分析系统(matlab源码)
  • Java设计模式-桥接模式
  • Alibaba Cloud Linux 3 在 Apple M 芯片 Mac 的 VMware Fusion 上部署的完整密码重置教程(二)
  • API 接口在电商中的重要应用​||关于API接口接入
  • 构建者设计模式 Builder
  • python学习DAY45打卡
  • 【运维实战】系统全链路监测方案~架构到实践
  • 【HTML】document api
  • 【每天学点‘音视频’】前向纠错 和 漏包重传
  • 图像分类精度评价的方法——误差矩阵、总体精度、用户精度、生产者精度、Kappa 系数
  • 在 PyCharm Notebook 中安装 YOLO
  • Google 的 Opal:重新定义自动化的 AI 平台
  • 【项目】分布式Json-RPC框架 - 项目介绍与前置知识准备
  • ARM架构下的cache transient allocation hint以及SMMUv2的TRANSIENTCFG配置详解
  • kafka 冲突解决 kafka安装
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘pygame’问题