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

【每日一题】Day5

文章目录

  • 15.三数之和
  • 题目
  • 思路
    • Go 内置函数:append
    • sort 包提供的函数:sort.Ints
  • 代码实现(Go)
  • 1.两数之和
  • 题目
  • 思路
    • 数据结构map[int]int
    • 遍历数组或切片range
  • 代码实现(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
-105 <= nums[i] <= 105


思路

1) 核心:把 3Sum 变成多个 2Sum

  • 先对数组排序:nums[0] <= nums[1] <= ... <= nums[n-1]
  • 枚举第一个数的下标 i,问题就变成在区间 (i, n-1] 里找两数之和 = -nums[i]
  • 对有序数组求 2Sum 的最优解就是双指针:左指针 L=i+1,右指针 R=n-1

这样每个 i 的子问题用 O(n) 找到所有解,整体 O(n^2)。


2) 排序 + 双指针更高效

单调性(这是双指针成立的关键):

  • 数组已排序,固定 i 后,当前和 s = nums[i] + nums[L] + nums[R]
  • s < 0,想让和变大,只能右移 L(更大的数)。
  • s > 0,想让和变小,只能左移 R(更小的数)。
  • s == 0,记录答案,然后把 L 向右跳过相同值,把 R 向左跳过相同值,再继续收缩(因为当前这两个值再参与只会得到重复三元组)。

所以,每次比较都能单向移动一个指针,不会回头,单次子问题是线性的。


3) 去重的三个“关口”

不重复是这个题的难点,正确的“去重点位”有 3 个:

  1. 固定点去重(外层 i)

    • i > 0nums[i] == nums[i-1],说明以 nums[i] 为首的所有三元组,之前以 nums[i-1] 为首时已经找过,直接 continue
  2. 命中解后左侧去重(L)

    • 当发现一组解 nums[i] + nums[L] + nums[R] == 0,把这组解加入结果;
    • 随后 for L < R && nums[L] == nums[L+1] { L++ }跳过左边重复的值,避免得到完全相同的三元组。
  3. 命中解后右侧去重(R)

    • 同理,for L < R && nums[R] == nums[R-1] { R-- }跳过右边重复的值

只有在已经命中一个解之后,才对 LR 做“跳过相同值”的去重。否则在 sum<0sum>0 的情况下,直接常规地移动一个指针即可(不需要跳过相同值,因为没产生解,自然不会重复记录)。


4) 正确性

  • 不漏

    • 对每个 i,在 (i, n-1] 上用双指针列举所有两数和为 -nums[i] 的组合;
    • 双指针的移动规则保证“所有可能的 (L, R)”都会按序被考虑到(除了被判断为重复而跳过的那些,它们对应的三元组与已记录的完全相同)。
  • 不重

    • i 的去重避免同一个首元素被重复固定;
    • 命中解后对 LR 的去重,避免同一首元素下,相同的第二、第三元素被重复组合;
    • 所以每个唯一三元组只会被记录一次。

排序建立单调、双指针利用单调、三处去重保证唯一


Go 内置函数:append

语法:slice = append(slice, elem1, elem2, ... )
slice:必须是切片(slice),不能是数组。
elem1, elem2, …:要追加的元素,可以是一个或多个。
返回值:返回一个新的切片(可能和原来的底层数组不同)。
必须接收返回值:所以常见写法是 s = append(nums, x)

sort 包提供的函数:sort.Ints

定义:func Ints(a []int) ,只能排序 []int,无返回值,默认升序
使用:import "sort" sort.Ints(nums)
参数:nums []int(一个 int 类型的切片


代码实现(Go)

详细注解:

//package main
//
//import (
//	"fmt"
//	"sort"
//)func threeSum(nums []int) [][]int {res := [][]int{}n := len(nums)// 特判if n < 3 {return res}// 1. 先排序sort.Ints(nums)// 2. 遍历数组,固定第一个数 nums[i]for i := 0; i < n-2; i++ {// 剪枝:如果当前数大于0,后面不可能找到和为0的三元组if nums[i] > 0 {break}// 跳过重复的已处理过的数,避免重复结果if i > 0 && nums[i] == nums[i-1] {continue}// 3. 定义双指针// 在 nums[i+1 ... n-1] 之间,找另外两个数,让三者加和为 0L, R := i+1, n-1for L < R {sum := nums[i] + nums[L] + nums[R]// 根据结果来移动指针if sum == 0 {res = append(res, []int{nums[i], nums[L], nums[R]})// 跳过重复的左指针for L < R && nums[L] == nums[L+1] {L++}// 跳过重复的右指针for L < R && nums[R] == nums[R-1] {R--}// 移动双指针,继续寻找L++R--} else if sum < 0 {// 总和太小,左指针右移L++} else {// 总和太大,右指针左移R--}}}return res
}//func main() {
//	nums := []int{-1, 0, 1, 2, -1, -4}
//	fmt.Println(threeSum(nums)) // 输出 [[-1 -1 2] [-1 0 1]]
//}

无注释:

//package main
//
//import (
//	"fmt"
//	"sort"
//)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 nums[i] > 0 {break}if i > 0 && nums[i] == nums[i-1] {continue}L, R := i+1, n-1for L < R {sum := nums[i] + nums[L] + nums[R]if sum == 0 {res = append(res, []int{nums[i], nums[L], nums[R]})for L < R && nums[L] == nums[L+1] {L++}for L < R && nums[R] == nums[R-1] {R--}L++R--} else if sum < 0 {L++} else {R--}}}return res
}//func main() {
//	nums := []int{-1, 0, 1, 2, -1, -4}
//	fmt.Println(threeSum(nums)) // 输出 [[-1 -1 2] [-1 0 1]]
//}


1.两数之和

题目

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案


思路

每次遍历一个数字 num,去找 target - num 是否在哈希表
如果找到了 → 返回 [之前存的下标, 当前下标]
如果没找到 → 把当前数字存入哈希表,备用
这样就能一次遍历找到答案,不需要双重循环

  • 如果当前元素的配对元素在它之前出现 → 可以在哈希表中找到
  • 如果当前元素的配对元素在它之后出现 → 当遍历到那个元素时,当前元素已经被加入哈希表 → 同样可以找到答案
  • 因此只要遍历数组一次,就能找到唯一答案

数据结构map[int]int

map 是 Go 的哈希表(字典)
key:整数类型 int → 存储数字值
value:整数类型 int → 存储该数字在数组中的下标
if j, ok := m[num2]; ok { ... }
返回两个值:
j → 哈希表中 num2 对应的下标
ok → 布尔值,表示 key num2 是否存在于 map 中

true → 存在
false → 不存在

遍历数组或切片range

for i, num1 := range nums { ... }
返回两个值:
i → 当前元素的下标(索引)
num1 → 当前元素的值

for _, num1 := range nums { ... } // 忽略索引
for i := range nums { ... } // 忽略值


代码实现(Go)

详细注解:

//package main
//
//import "fmt"func twoSum(nums []int, target int) []int {// 创建哈希表,key: 数字值,value: 数字在数组中的下标m := make(map[int]int)for i, num1 := range nums { // range返回索引,值num2 := target - num1     // 需要的另一个数字if j, ok := m[num2]; ok { // j :哈希表中 nums2 的 下标// 如果哈希表中存在 num2,说明找到了答案return []int{j, i}}// 否则,把当前数字及索引存入哈希表m[num1] = i}return nil // 返回空数组 nil 代替空切片
}//func main() {
//	nums := []int{2, 7, 11, 15}
//	target := 9
//	result := twoSum(nums, target)
//	fmt.Println(result) // 输出 [0, 1]
//}

无注释:

//package main
//
//import "fmt"func twoSum(nums []int, target int) []int {m := make(map[int]int)for i := 0; i < len(nums); i++ {num1 := nums[i]num2 := target - num1if j, ok := m[num2]; ok {return []int{j, i}}m[num1] = i}return nil
}//func main() {
//	nums := []int{2, 7, 11, 15}
//	target := 9
//	result := twoSum(nums, target)
//	fmt.Println(result) // 输出 [0, 1]
//}

http://www.lryc.cn/news/622868.html

相关文章:

  • 电路设计——复位电路
  • 设计模式之静态代理
  • Java 10 新特性及具体应用
  • ABB焊接机器人弧焊省气
  • 多机编队——(6)解决机器人跟踪过程中mpc控制转圈问题
  • 【轨物方案】预防性运维:轨物科技用AI+机器人重塑光伏电站价值链
  • MyBatis极速通关中篇:核心配置精讲与复杂查询实战
  • 大模型教机器人叠衣服:2025年”语言理解+多模态融合“的智能新篇
  • Tomcat架构深度解析:从Server到Servlet的全流程揭秘
  • blender制作动画导入unity两种方式
  • ENSP的简单动态路由rip协议配置
  • 广东省省考备考(第七十八天8.16)——资料分析、判断推理(强化训练)
  • Docker目录的迁移
  • GaussDB 数据库架构师修炼(十三)安全管理(3)-行级访问控制
  • 6JSON格式转python并实现数据可视化
  • 在ubuntu系统上离线安装jenkins的做法
  • 零基础学习人工智能的完整路线规划
  • Flink Stream API 源码走读 - window 和 sum
  • (第十七期)HTML图像标签详解:从入门到精通
  • 【完整源码+数据集+部署教程】高尔夫球追踪与识别系统源码和数据集:改进yolo11-LAWDS
  • 【基础-判断】可以通过ohpm uninstall 指令下载指定的三方库
  • 力扣(接雨水)——基于最高柱分割的双指针
  • 【开发技巧】VS2022+QT5+OpenCV4.10开发环境搭建QT Creator
  • 肖臻《区块链技术与应用》第23-26讲 - The DAO事件、BEC事件、反思和总结
  • Qt 关于QString和std::string数据截断的问题- 遇到\0或者0x00如何处理?
  • ★CentOS:MySQL数据备份
  • 三天速通 Vue+Flask+SQLite 项目+阿里云轻量应用级服务器【宝塔面板】②
  • 数学建模Topsis法笔记
  • TOGAF八步一法笔记2
  • 【DL学习笔记】常用数据集总结