LeetCode算法“无重复字符的最长子串”哈希表+滑动窗口+贪心

要解决 "最长无重复字符子串" 问题,我们可以通过滑动窗口算法 结合哈希表高效求解。以下是详细的解题步骤,包括问题分析、算法设计、具体实现和边界处理:

一、问题分析

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

  • 输入 s = "abcabcbb",输出 3(最长子串为 "abc""bca" 等,长度均为 3)。
  • 输入 s = "bbbbb",输出 1(唯一无重复子串为单个 "b")。
  • 输入 s = "",输出 0(空字符串无有效子串)。

二、核心算法:滑动窗口 + 哈希表

1. 滑动窗口的基本思想

滑动窗口是一种通过双指针 (左指针 left 和右指针 right)界定 "当前子串范围" 的技术。两个指针之间的区域称为 "窗口",通过移动指针动态调整窗口大小,以寻找满足条件(无重复字符)的最长子串。

  • 右指针 right:负责 "扩展窗口",不断向右移动以纳入新字符,尝试扩大子串长度。
  • 左指针 left:负责 "收缩窗口",当右指针遇到重复字符时,左指针向右移动,直到窗口内不再包含重复字符,保证窗口的有效性。
2. 哈希表的作用

为了快速判断 "右指针新纳入的字符是否已在当前窗口中",我们使用哈希表(本题中用大小为 128 的数组,对应 ASCII 字符集)记录字符在窗口中的存在状态:

  • 数组索引 = 字符的 ASCII 值(如 'a' 的 ASCII 值为 97,对应索引 97)。
  • 数组值 = 1 表示字符 "在当前窗口中",0 表示 "不在窗口中"。
  • 优势:判断字符是否重复的时间复杂度为 O(1),比遍历子串检查(O (n))高效得多。

三、详细解题步骤

以示例 s = "abcabcbb" 为例,逐步演示算法执行过程:

步骤 1:初始化
  • 左指针 left 和右指针 right 均指向字符串起始位置(left = right = 0,对应字符 'a')。
  • 哈希表 hash[128] 初始化全为 0(表示所有字符初始状态为 "不在窗口中")。
  • 变量 max_len 记录最长子串长度,初始为 0。
步骤 2:扩展右指针,纳入新字符

循环执行以下操作,直到 right 到达字符串末尾(*right == '\0'):

① 检查新字符是否重复

右指针当前指向的字符为 s[right],通过哈希表判断:

  • hash[s[right]] == 0:字符未重复,可直接纳入窗口。
  • hash[s[right]] == 1:字符已在窗口中,需先收缩左指针。
② 收缩左指针(若有重复)

当新字符重复时,左指针 left 向右移动,同时将移出窗口的字符从哈希表中移除(标记为 0),直到重复字符被移出窗口:

  • 例如,当 right 指向第 3 个字符 'a'(索引 3)时,哈希表中 hash['a'] = 1(因为 left 仍在索引 0,'a' 已在窗口中)。
  • 此时左指针右移(left = 1),并将 s[0] = 'a' 从哈希表中移除(hash['a'] = 0),窗口中不再有重复的 'a'
③ 纳入新字符并更新最长长度

将右指针指向的字符纳入窗口(hash[s[right]] = 1),计算当前窗口长度(right - left + 1),若大于 max_len 则更新 max_len

④ 右指针右移,继续扩展

重复上述步骤,直到 right 遍历完所有字符。

四、示例执行过程(s = "abcabcbb"

步骤 right 指向 字符 是否重复 左指针调整 窗口范围 当前长度 max_len
1 0('a' 'a' [0,0] 1 1
2 1('b' 'b' [0,1] 2 2
3 2('c' 'c' [0,2] 3 3
4 3('a' 'a' 是(窗口有 'a') left→1 [1,3] 3 3
5 4('b' 'b' 是(窗口有 'b') left→2 [2,4] 3 3
6 5('c' 'c' 是(窗口有 'c') left→3 [3,5] 3 3
7 6('b' 'b' 是(窗口有 'b') left→4 [4,6] 3→2(left→5 后) 3
8 7('b' 'b' 是(窗口有 'b') left→6 [6,7] 2→1(left→7 后) 3

最终 max_len = 3,与预期结果一致。

五、代码实现(修正后)

cpp 复制代码
int lengthOfLongestSubstring(char* s) {
    int hash[128]={0};

    int max = 0;
    char *left = s;
    char *right = s;  //左右指针初始位置均在S第一个字符

    while(*right!='\0'){
       
        while(hash[*right]) //直至重复元素对的前者被剔除
        {
            hash[*left]=0; //哈希表中删除左元素
            left++;
        }
        hash[*right] = 1;  //添加该右元素

        if(right-left+1>max)
        max = right-left+1;

         right++;   //右移
    }
return max;
}

六、关键细节与边界处理

  1. 空字符串 :若 s 为空(*s == '\0'),循环不执行,直接返回 0,避免指针越界。
  2. 单字符字符串 :如 s = "a",右指针处理 'a' 后,窗口长度为 1,max_len = 1
  3. 全重复字符 :如 s = "aaaaa",每次右指针移动都会触发左指针收缩(始终与右指针相邻),窗口长度恒为 1,max_len = 1
  4. 字符类型转换 :用 (unsigned char)*right 确保 ASCII 值为 0~255 的字符正确映射到数组索引(避免负数索引)。

七、时间与空间复杂度

  • 时间复杂度 :O (n),其中 n 是字符串长度。每个字符最多被 leftright 各访问一次,总操作次数为 O (n)。
  • 空间复杂度:O (1),哈希表大小固定为 128(ASCII 字符集),与输入规模无关。

通过滑动窗口动态调整和哈希表快速判断,该算法高效解决了 "最长无重复字符子串" 问题,兼顾了时间和空间效率。

作者:牢笼

链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solutions/3793743/cyu-yan-jie-jue-wu-zhong-fu-zi-fu-de-zui-yjcy/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关推荐
hn小菜鸡2 小时前
LeetCode 1023.驼峰式匹配
数据结构·算法·leetcode
不枯石3 小时前
Matlab通过GUI实现点云的导向(引导)滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab
程序员三明治3 小时前
二分查找思路详解,包含二分算法的变种,针对不同题的做法
java·数据结构·算法·二分查找
xiaoningaijishu3 小时前
MATLAB中的Excel文件操作:从入门到精通
其他·算法·matlab·excel
未知陨落3 小时前
LeetCode:67.寻找旋转排序数组中的最小值
数据结构·算法·leetcode
Gu_yyqx3 小时前
快速排序总结
数据结构·算法·排序算法
Haooog4 小时前
111.二叉树的最小深度(二叉树算法题)
java·数据结构·算法·二叉树
地平线开发者4 小时前
模型插入 NV12 预处理节点精度问题排查流程
算法·自动驾驶
我要学习别拦我~4 小时前
逻辑回归中的成本损失函数全解析:从数学推导到实际应用
算法·机器学习·逻辑回归