力扣解题-无重复字符的最长子串

力扣解题-无重复字符的最长子串

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

示例 1: 输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。注意 "bca" 和 "cab" 也是正确答案。

示例 2: 输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

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

提示: 0 <= s.length <= 5 * 104

s 由英文字母、数字、符号和空格组成


第一次解答

解题思路

核心方法:滑动窗口(双指针) + 哈希集合去重,通过动态调整窗口的左右边界,在一次遍历中找到无重复字符的最长子串,将暴力解法的O(n²)时间复杂度优化至O(n),是该问题的经典最优解法。

具体步骤:

  1. 核心原理铺垫 :滑动窗口是解决"子串/子数组"类问题的常用技巧,本题中用leftright两个指针分别表示窗口的左、右边界,窗口[left, right]内始终维护一个"无重复字符的子串",通过动态调整边界来找到最长窗口长度。
  2. 初始化关键变量
    • seenHashSet<Character>集合,用于快速判断当前字符是否已在窗口内(O(1)查找效率),存储当前窗口内的所有字符;
    • left:窗口左指针,初始值为0,代表窗口起始位置;
    • maxLength:记录遍历过程中找到的最长无重复子串长度,初始值为0。
  3. 右指针遍历扩展窗口
    • 右指针right从0开始遍历字符串的每个字符c = s.charAt(right),代表尝试将当前字符加入窗口;
    • 若当前字符c已存在于seen集合中(说明窗口内有重复字符),则进入循环收缩左边界:
      • 移除左指针left指向的字符(seen.remove(s.charAt(left)));
      • 左指针left右移一位(left++);
      • 重复上述操作,直到seen中不再包含c(此时窗口内恢复无重复状态)。
    • 将当前字符c加入seen集合(此时c是窗口新的右边界字符);
    • 计算当前窗口的长度(right - left + 1),并更新maxLength为"当前最大值"和"窗口长度"中的较大者。
  4. 返回结果 :遍历完成后,maxLength即为无重复字符的最长子串长度,直接返回。

核心优化逻辑说明

  1. 时间复杂度优化
    • 暴力解法需枚举所有子串(O(n²)),并对每个子串检查是否有重复(O(n)),总时间复杂度O(n³),无法适配n=5×10⁴的规模;
    • 滑动窗口仅需一次遍历(右指针遍历n次),左指针最多也移动n次,整体时间复杂度为O(n)(每个字符最多被左/右指针各访问一次),完全满足题目性能要求。
  2. 空间复杂度说明
    • 该解法用HashSet存储窗口内字符,最坏情况下(字符串无重复)需存储n个字符,空间复杂度为O(n),这是"时间换空间"的合理权衡;
    • 若需进一步优化空间,可改用数组(ASCII字符范围)替代HashSet,但核心逻辑不变,仅存储方式不同。
  3. 性能表现说明:
    • 耗时5ms击败32.91%的用户:滑动窗口是该题的标准最优解,多数提交均采用此逻辑,耗时差异主要来自评测机运行环境(如JVM的即时编译、系统资源占用等),该耗时已属于高效范畴;
    • 内存消耗76MB击败19.06%的用户:核心原因是HashSet的底层存储(如HashMap的数组+链表/红黑树结构)存在额外内存开销,若改用数组存储字符是否存在,可降低内存消耗。

执行耗时:6 ms,击败了61.68% 的Java用户

内存消耗:45.9 MB,击败了13.31% 的Java用户

java 复制代码
public int lengthOfLongestSubstring(String s) {
        Set<Character> seen = new HashSet<>();
        int left = 0, maxLength = 0;

        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            while (seen.contains(c)) {
                seen.remove(s.charAt(left));
                left++;
            }
            seen.add(c);
            maxLength = Math.max(maxLength, right - left + 1);
        }
        return maxLength;
    }

总结

  1. 该解法的核心是滑动窗口动态维护无重复子串:右指针扩展窗口,左指针收缩窗口以消除重复,确保窗口内始终无重复字符,一次遍历即可找到最长长度;
  2. HashSet的O(1)查找效率是滑动窗口能高效运行的关键,避免了每次检查重复都遍历窗口的冗余操作;
  3. 滑动窗口的时间复杂度O(n)是该问题的最优下界,无法进一步优化,该解法已达到理论最优性能。
相关推荐
2601_962055978 小时前
跟据spring boot版本,查看对应的tomcat,并查看可支持的tomcat的版本范围
spring boot·后端·tomcat
裕晟资质规划8 小时前
武器装备科研生产单位保密资质申请方法论:条件模型与流程拆解
人工智能·算法
Lost of 程序猿9 小时前
ASP.NET Core API 幂等性设计深度实战:从一次重复申领事故说起
后端·asp.net
en.en..9 小时前
C语言 标准输入 / 输出缓冲区
算法
QQ_216962909611 小时前
【源码编号:project79475】SpringBoot校内二手交易平台:商品发布、分类检索、留言交流、订单管理全流程实战
java·spring boot·后端
吃好睡好便好11 小时前
查找函数的使用
学习·算法·matlab·生活·查找函数
学习星球11 小时前
单调栈——从“找下一个更大的“到柱状图中的最大矩形
数据库·c++·算法·leetcode·xcode
郑州光合科技余经理12 小时前
本地生活服务系统:模块边界与结算字段怎么拆
java·开发语言·前端·后端·系统架构·uni-app·php
IT_陈寒12 小时前
Vue的嵌套组件竟然吃掉了我的事件?
前端·人工智能·后端
靠沿13 小时前
贪心算法专题(二)
算法·贪心算法