LeetCode 003. 无重复字符的最长子串 - 滑动窗口与哈希表详解

一、文章标题

LeetCode 003. 无重复字符的最长子串 - 滑动窗口与哈希表详解

二、文章内容

1. 题目概述
2. 文章目录

目录

  1. 题目概述
  2. 解题思路
  3. 算法详解
    • 3.1 [解法一:暴力枚举 + 去重检查](#解法一:暴力枚举 + 去重检查)
    • 3.2 [解法二:滑动窗口(HashMap 下标跳跃)](#解法二:滑动窗口(HashMap 下标跳跃))
  4. 解法对比
  5. 最优解推荐
3. 解题思路
  • 问题分析:
    • 输入:一个字符串 s(可包含字母、数字、符号等)。
    • 输出:s 中不含重复字符的最长子串的长度(整数)。
    • 约束:子串必须连续;字符一旦重复,需移动左边界重新保持无重复。
  • 核心难点:
    • 如何在扫描过程中高效检测并维护"无重复"的窗口。
    • 当右侧遇到重复字符时,如何快速将左指针跳到正确位置,避免一个个挪动导致退化为 O(n^2)。
  • 解题方向:
    1. 暴力:枚举所有起点,向右扩张直到出现重复,更新答案。
    2. 滑动窗口 + HashSet:维护一个无重复窗口,重复时左指针逐步右移并移除字符。
    3. 滑动窗口 + HashMap(最优):记录每个字符最近一次出现的位置,遇到重复时左指针一次性跳跃到重复字符的下一个位置。
4. 算法详解

解法一:暴力枚举 + 去重检查 {#解法一}

算法原理

  • 基本思想:固定起点 i,使用一个集合记录已出现字符,从 i 向右扩张 j,遇到重复则停止,更新答案。
  • 适用场景:数据量较小、用于直观理解问题结构与边界情况。

具体实现

  • 步骤1:特殊情况处理,若 s 为空或长度为 0,返回 0。
  • 步骤2:外层循环遍历起点 i,内层使用 HashSet 从 i 开始扩张 j,直到遇到重复。
  • 步骤3:过程中维护最长长度 maxLen。

复杂度分析

  • 时间复杂度:O(n^2),最坏情况下(如全不重复),每个起点都可能扫描到末尾。
  • 空间复杂度:O(min(n, Σ)),Σ 为字符集大小,集合中最多存储当前子串的字符数。

Java代码实现

java 复制代码
class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 边界处理:空字符串或null,直接返回0
        if (s == null || s.length() == 0) {
            return 0;
        }
        int n = s.length();
        int maxLen = 0;
        // 外层固定起点 i
        for (int i = 0; i < n; i++) {
            java.util.HashSet<Character> seen = new java.util.HashSet<>();
            // 从 i 向右扩张 j
            for (int j = i; j < n; j++) {
                char c = s.charAt(j);
                if (seen.contains(c)) {
                    // 遇到重复字符,当前起点下的最长子串结束
                    break;
                }
                seen.add(c);
                // 更新答案
                int currLen = j - i + 1;
                if (currLen > maxLen) {
                    maxLen = currLen;
                }
            }
        }
        return maxLen;
    }
}

解法二:滑动窗口(HashMap 下标跳跃) {#解法二}

算法原理

  • 基本思想:使用双指针 [left, right] 表示当前无重复窗口,HashMap 记录每个字符最近一次出现的下标。
  • 当右指针读到重复字符 c 时,将 left 跳到 max(left, lastIndex[c] + 1),确保窗口内无重复。
  • 适用场景:通用最优解,线性时间,思路清晰,易于面试手写。

具体实现

  • 步骤1:特殊情况处理,s 为空或长度为 0 返回 0。
  • 步骤2:维护 HashMap<Character, Integer> lastIndex,left 指向当前窗口左边界,遍历 right 从 0 到 n-1。
  • 步骤3:若当前字符 c 在 map 中且上次位置 >= left,则更新 left 为上次位置 + 1;随后更新 map 中 c 的下标为 right,并维护答案。

复杂度分析

  • 时间复杂度:O(n),每个字符至多被左右指针各访问/更新常数次。
  • 空间复杂度:O(min(n, Σ)),Σ 为字符集大小,map 存储窗口中出现过的字符下标。

Java代码实现

java 复制代码
import java.util.HashMap;

class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 边界处理:若字符串为 null 或长度为 0,则没有有效子串,返回 0
        if (s == null || s.length() == 0) {
            return 0;
        }

        // lastIndex 用于记录每个字符最近一次出现的位置(下标)
        HashMap<Character, Integer> lastIndex = new HashMap<>();
        int left = 0;   // 当前无重复窗口的左边界
        int maxLen = 0; // 结果:最长无重复子串长度

        // 使用 right 指针遍历字符串的每个字符
        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);

            // 如果字符 c 出现过,并且它上一次出现位置在当前窗口内
            if (lastIndex.containsKey(c)) {
                int prev = lastIndex.get(c);
                // 将 left 跳到重复字符的下一位,但不能左移(保持单调不回退)
                if (prev + 1 > left) {
                    left = prev + 1;
                }
            }

            // 更新当前字符的最新出现位置
            lastIndex.put(c, right);

            // 计算当前窗口长度并更新答案
            int currLen = right - left + 1;
            if (currLen > maxLen) {
                maxLen = currLen;
            }
        }

        return maxLen;
    }
}
5. 解法对比 {#解法对比}
解法 时间复杂度 空间复杂度 优点 缺点 适用场景
暴力枚举 + 去重检查 O(n^2) O(min(n, Σ)) 思路直观、实现简单 性能较差,无法通过大数据量 入门理解、数据量小
滑动窗口(HashMap 下标跳跃) O(n) O(min(n, Σ)) 线性时间、实现清晰、面试高频 需理解左指针"跳跃"细节 通用最优、面试推荐
6. 最优解推荐 {#最优解推荐}
  • 最优解推荐:滑动窗口(HashMap 下标跳跃)。
  • 原因:在保证 O(n) 时间复杂度的同时,代码可读性强,空间使用可控,适合在面试中快速、稳定地实现与讲解。

三、文章标签

算法,哈希表,滑动窗口,双指针,字符串,LeetCode,中等

四、文章简述

经典字符串题,利用滑动窗口 + 哈希表实现 O(n) 线性解,关键在于用下标映射实现左指针跳跃,避免逐步移动导致退化。适合准备面试与夯实基础的同学,代码注释详尽,便于快速掌握与复盘。

相关推荐
王璐WL3 小时前
【c++】c++第一课:命名空间
数据结构·c++·算法
空白到白3 小时前
机器学习-聚类
人工智能·算法·机器学习·聚类
索迪迈科技3 小时前
java后端工程师进修ing(研一版 || day40)
java·开发语言·学习·算法
zzzsde4 小时前
【数据结构】队列
数据结构·算法
芒克芒克4 小时前
LeetCode 面试经典 150 题:删除有序数组中的重复项(双指针思想解法详解)
算法
青 .4 小时前
数据结构---二叉搜索树的实现
c语言·网络·数据结构·算法·链表
MChine慕青4 小时前
顺序表与单链表:核心原理与实战应用
linux·c语言·开发语言·数据结构·c++·算法·链表
塔中妖5 小时前
【华为OD】查找接口成功率最优时间段
算法·链表·华为od
塔中妖5 小时前
【华为OD】最大子矩阵和
算法·华为od·矩阵