当前位置: 首页 > 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.dtcms.com/a/335099.html

相关文章:

  • 一键检测接口是否存活:用 Python/Shell 写个轻量级监控脚本
  • 后量子密码算法ML-KEM介绍及开源代码实现
  • 数据结构:二叉平衡树
  • ROS move_base 混合功能导航 RealSense D435i + 3D 点云地图 + 楼层切换 + 路径录制 + 路径规划
  • 微服务的编程测评系统12-xxl-job-历史竞赛-竞赛报名
  • C++ 力扣 1658.将 x 减到 0 的最小操作数 题解 优选算法 滑动窗口 (同向双指针)优化 每日一题 详细题解
  • 免费万能电子书格式转换器!Neat Converter支持 ePub、Azw3、Mobi、Doc、PDF、TXT 文件的相互转换。
  • 两个简单的设计模式的例子
  • FP16(半精度)和FP32(单精度)
  • CentOS7安装使用FTP服务
  • 产品设计.原型设计
  • TexStudio中的Latex,PDFLatex,XeLatex和LuaLatex的区别
  • vue优化有哪些手段?
  • ubuntu网络共享
  • 深度解析和鲸社区热门项目:电商双 11 美妆数据分析的细节与价值
  • ubuntu远程桌面很卡怎么解决?
  • [Linux] Linux系统负载监控 Linux服务管理
  • Java Stream sort算子实现:SortedOps
  • 网络层(1)
  • DeepResearch开源与闭源方案对比
  • autofit.js: 自动调整HTML元素大小的JavaScript库
  • 小智-ESP32的MQTT协议
  • linux设备驱动之字符设备驱动
  • Python-Pandas基础
  • 主从复制+哨兵
  • 移动互联网发展战略
  • Altium Designer 22使用笔记(7)---网表导入,叠层设置
  • Spring框架(IOC)
  • 程序设计|C语言教学——C语言基础4:进阶
  • TOGAF八步一法笔记2