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

【LeetCode每日一题】

每日一题

  • 3. 无重复字符的最长子串
    • 题目
    • 总体思路
    • 代码
  • 1.两数之和
    • 题目
    • 总体思路
    • 代码
  • 15. 三数之和
    • 题目
    • 总体思路
    • 代码

2025.8.15

3. 无重复字符的最长子串

题目

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

总体思路

用布尔数组的滑动窗口法

  • 使用固定大小数组标记字符是否出现过:
    这里 visited[128] 对应 ASCII 码字符。
    true 表示该字符在当前窗口内已经出现。
  • 维护滑动窗口:
    left 表示窗口左边界,right 表示窗口右边界。
    窗口 [left, right] 中保证没有重复字符。
  • 遇到重复字符时移动左指针:
    如果 visited[ch] == true,说明当前字符已在窗口中。
    移动左指针 left,同时将左边字符标记清除,直到窗口中不再有重复字符。
  • 更新窗口状态:
    将当前字符 ch 标记为已出现。
    更新 maxLen 为窗口长度 right-left+1。
  • 时间复杂度:
    每个字符最多进出窗口一次 → O(n)
    空间复杂度 O(1),因为固定数组大小 128。

用哈希表的滑动窗口法

  • 使用哈希表记录窗口中字符出现次数:
    键是字符,值是出现次数(这里最多是 1)。
    动态维护当前窗口的字符集合。
  • 维护滑动窗口的左右指针:
    i 是左指针,rk 是右指针。
    左指针每移动一次,移除窗口最左边的字符。
    右指针尽可能右移,保证窗口内没有重复字符。
  • 右指针移动条件:
    rk+1 < n 防止越界
    m[s[rk+1]] == 0 窗口中没有重复字符
    满足条件时,右指针 rk 右移,并将字符计入哈希表。
  • 更新答案:
    窗口 [i, rk] 是以 i 为左边界的最长无重复字符子串
    ans = max(ans, rk-i+1)。
  • 时间复杂度:
    每个字符进出窗口一次 → O(n)
    空间复杂度 O(min(n, charset)),因为哈希表动态存储字符。

代码

golang

// 使用滑动窗口法查找无重复字符的最长子串长度
func lengthOfLongestSubstring(s string) int {visited := [128]bool{} // ASCII 码范围的字符标记数组left, maxLen := 0, 0for right := 0; right < len(s); right++ {ch := s[right]// 如果当前字符已经在窗口内出现过,则移动左指针直到移除该字符for visited[ch] {visited[s[left]] = falseleft++}// 标记该字符已出现visited[ch] = true// 更新最大长度if right-left+1 > maxLen {maxLen = right - left + 1}}return maxLen
}func lengthOfLongestSubstring(s string) int {// 哈希表,记录窗口中字符出现次数m := map[byte]int{}n := len(s) // 字符串长度// 右指针,初始值为 -1// 相当于在字符串左边界左侧,还没有开始移动rk, ans := -1, 0// 遍历每个字符,i 是左指针for i := 0; i < n; i++ {if i != 0 {// 左指针向右移动一格// 移除窗口最左边的字符delete(m, s[i-1])}// 不断向右移动右指针 rk// 条件:// 1. rk+1 < n,防止越界// 2. m[s[rk+1]] == 0,窗口中没有重复字符for rk+1 < n && m[s[rk+1]] == 0 {rk++              // 右指针右移一格m[s[rk]]++        // 把该字符加入窗口}// 此时窗口 [i, rk] 是一个最长无重复字符子串// 更新答案ans = max(ans, rk-i+1)}return ans
}

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
只会存在一个有效答案

总体思路

暴力求解法

  • 枚举所有可能的两个数:
    用两层 for 循环,第一层遍历第一个数 nums[i],第二层遍历第二个数 nums[j]。
  • 检查是否满足条件:
    如果 nums[i] + nums[j] == target 并且 i != j,说明找到了一对符合条件的下标。
  • 立即返回结果:
    直接返回这两个下标 [i, j]。
  • 时间复杂度:
    外层循环 O(n),内层循环 O(n) → 总体 O(n²)。
    空间复杂度 O(1),因为没有额外的数据结构。

哈希表优化法

  • 使用哈希表记录已访问过的数值:
    建一个 map[int]int,键是数值,值是该数值在 nums 中的下标。
  • 单次遍历寻找匹配:
    遍历 nums 时,对于当前数 num,计算它的“互补数” target - num。
  • 检查互补数是否已出现过:
    如果互补数在哈希表中,说明之前某个元素的值 + 当前值正好等于 target,直接返回这两个下标。
  • 否则记录当前数:
    把当前的 (数值 → 下标) 存进哈希表,供后续元素匹配。
  • 时间复杂度:
    仅需单次遍历 O(n)。
    空间复杂度 O(n),用来存储哈希表。

代码

golang

//暴力求解
func twoSum(nums []int, target int) []int {for i:=0;i<len(nums);i++{for j:=1;j<len(nums);j++{if nums[i]+nums[j]==target && i!=j{return []int{i,j}}}}return nil
} //哈希
func twoSum(nums []int, target int) []int {hash:=map[int]int{}   //键为“某个数值”,值为“该数值在 nums 中的下标”。for i, num:=range nums {   //range 遍历切片,i 是下标,num 是当前元素的拷贝if p, ok :=hash[target-num];ok{   //互补数是否已经出现过。如果出现过,ok == true,p 是互补数的下标。return []int{p,i}}hash[num]=i   //把“当前数值 → 当前下标”记进表里,供后面的元素来匹配。}return nil
}

2025.8.16

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

总体思路

双指针法

  • 排序
    先对数组 nums 排序。
    排序后可以利用双指针移动来快速缩小范围。
  • 固定第一个数 nums[i]
    从左到右依次枚举 i。
    如果 nums[i] > 0,因为数组已经排序,后面的数都 ≥0,不可能再凑出 0,可以直接停止。
    如果 nums[i] 跟前一个数相同,就跳过,避免重复解。
  • 左右指针搜索另外两个数
    设定 left := i+1,right := n-1。
    计算三数之和 sum := nums[i] + nums[left] + nums[right]。
    • 如果 sum == 0,说明找到一组解。
      保存三元组 [nums[i], nums[left], nums[right]]。
      然后移动 left++ 和 right–,并且跳过重复值。
    • 如果 sum < 0,说明和太小,需要增大和 → left++。
    • 如果 sum > 0,说明和太大,需要减小和 → right–。
  • 继续枚举下一个 i
    直到遍历完成,返回所有解。

暴力求解(三重循环)

  • 枚举所有三元组
    用三层循环,依次固定下标 i、j、k,保证它们互不相等。
    枚举所有可能的组合 (nums[i], nums[j], nums[k])。
  • 判断和是否为 0
    如果 nums[i] + nums[j] + nums[k] == 0,就认为它是一个符合要求的三元组。
  • 避免重复
    直接三重循环会出现重复解,比如 [−1,0,1] 可能通过不同的下标组合出现多次。
    常见做法是:
    • 先对数组排序。
    • 再用一个 map 或 set(在 Go 里用 map[[3]int]bool)来记录已经出现过的三元组。
  • 保存结果:
    把符合条件的三元组存入结果切片 [][]int,最后返回。

代码

golang

//双指针
import "sort"
func threeSum(nums []int) [][]int {sort.Ints(nums)  //排序n := len(nums)//left, right := 1, len(nums)-1ret := make([][]int, 0)seen := make(map[[3]int]bool)for i:=0; i<n-2; i++ {// 如果 nums[i] > 0,后面全是非负数,不可能和为0,直接breakif nums[i] > 0 {break}// 跳过重复的i,避免结果重复if i > 0 && nums[i] == nums[i-1] {continue}left, right := i+1, n-1for left<right {sum := nums[i] + nums[left] + nums[right]if sum == 0 {key:=[3]int{nums[i],nums[left],nums[right]}if !seen[key] {seen[key]=trueret = append(ret,[]int{nums[i],nums[left],nums[right]})}left++right--}else if sum < 0 {left++ // 和偏小,左指针右移} else {right-- // 和偏大,右指针左移}}}return ret}//暴力求解超时了。。。bunengyong
import "sort"
func threeSum(nums []int) [][]int {sort.Ints(nums)    //先排序,便于存入集合时用有序三元组作为去重键res := make([][]int, 0)// 使用 map 记录已经出现过的三元组(用固定顺序的[3]int作为key)seen :=make(map[[3]int]bool)for i:=0;i<len(nums)-2;i++ {for j:=i+1;j<len(nums)-1;j++ {for k:=j+1;k<len(nums);k++ {if(nums[i]+nums[j]+nums[k]==0){key := [3]int{nums[i],nums[j],nums[k]}//去重if !seen[key]{   seen[key]=trueres = append(res, []int{nums[i], nums[j], nums[k]})}}}}}return res
}
http://www.dtcms.com/a/334221.html

相关文章:

  • Mac (三)如何设置环境变量
  • 从希格斯玻色子到 QPU:C++ 的跨维度征服
  • 代码随想录Day52:图论(孤岛的总面积、沉没孤岛、水流问题、建造最大岛屿)
  • 在ubuntu系统上离线安装jenkins的做法
  • 立体匹配中的稠密匹配和稀疏匹配
  • 8.16 pq
  • [系统架构设计师]系统质量属性与架构评估(八)
  • 解锁JavaScript性能优化:从理论到实战
  • 【完整源码+数据集+部署教程】太阳能面板污垢检测系统源码和数据集:改进yolo11-RVB-EMA
  • 地级市+省级气候政策不确定性指数(2000-2023年)-实证数据
  • ollama 自定义模型
  • imx6ull-驱动开发篇27——Linux阻塞和非阻塞 IO(上)
  • 【JS】认识并实现一个chrome扩展程序
  • 如何在 MacOS 上安装 SQL Server
  • MySQL完整重置密码流程(针对 macOS)
  • 硬核北京 | 2025世界机器人大会“破圈”,工业智能、康养科技…… 亦庄上演“机器人总动员”
  • Flink Sql 按分钟或日期统计数据量
  • 中本聪思想与Web3的困境:从理论到现实的跨越
  • 存算分离与云原生:数据平台的新基石
  • 基于Kubernetes亲和性与反亲和性的Pod调度优化实践指南
  • Linux上配置环境变量
  • 从频繁告警到平稳发布:服务冷启动 CPU 风暴优化实践01
  • Trae中`settings.json`文件的Java配置项功能详解(一)
  • Camera相机人脸识别系列专题分析之十九:MTK ISP6S平台FDNode原生代码
  • 【vscode使用说明】
  • Vue中的数据渲染【4】
  • Docker自定义镜像
  • 138-基于FLask的重庆市造价工程信息数据可视化分析系统
  • Chrome腾讯翻译插件transmart的安装
  • RK3588芯片在AR眼镜中的核心技术优势是什么?