likes
comments
collection
share

浅谈算法题:无重复字符的最长子串

作者站长头像
站长
· 阅读数 24

在算法学习的旅程中,解决字符串处理问题是不可或缺的一环。其中,“无重复字符的最长子串”作为一道经典问题,不仅考验着程序员对字符串操作的基本功,还涉及到了动态规划、滑动窗口等高级算法思想的巧妙运用。本文将深入浅出地探讨这个问题,从基本思路出发,逐步优化解法,并引入JavaScript语言中的Set数据结构,为读者呈现一种高效且易于理解的解决方案。

浅谈算法题:无重复字符的最长子串

问题概述

给定一个字符串s,任务是找出该字符串中不含有重复字符的最长连续子串的长度。例如,给定字符串s = "abcabcbb",最长无重复字符的子串是"abc",其长度为3。如果s = "bbbbb",最长子串就是"b",长度为1;对于s = "pwwkew",最长子串为"wke",长度为3,注意子串中不包含重复字符。

浅谈算法题:无重复字符的最长子串

基础解法分析

解法1:暴力枚举

最直观的想法是遍历字符串中的每一个字符,检查以该字符开头的子串是否含有重复字符。具体实现如笔记所述,通过创建一个新数组来存储每个字符,遇到重复则停止并记录当前子串长度,然后继续遍历下一个字符。这种方法简单直接,但效率低下,特别是当字符串很大时,时间复杂度达到O(n^3),因为每次检查重复都需要遍历数组。

var lengthOfLongestSubstring = function(s) {
    let max=0;
    for(let i=0;i<s.length;i++){
        let arr=[];
        for(let j=i;j<s.length;j++){
            if(arr.indexOf(s[j])==-1){
                arr.push(s[j]);
            }else{
                break;
            }
        }
        if(arr.length>max){
            max=arr.length;
        }
    }
    return max;
};

结果

浅谈算法题:无重复字符的最长子串

优化解法1:双指针技巧

认识到暴力解法的低效,我们尝试优化。引入左右两个指针(或称为滑动窗口),左指针代表当前子串的起始位置,右指针则不断向右探索,寻找新的字符加入子串。一旦发现重复,左指针向前移动一位,缩小窗口,继续探索。这种方式避免了重复计算,将时间复杂度降到了O(n^2),尽管仍非最优,但已是显著进步。

var lengthOfLongestSubstring = function(s) {
    let max=0;
    let left
    let right=0;
    let arr=[];
    for(left=0;left<s.length;left++){
        if(left>0){
            arr.shift();
        }
        for(let j=right;j<s.length;j++){
            if(arr.indexOf(s[j])==-1){
                arr.push(s[j]);
            }else{
                right=j;
                break;
            }
        }
        if(arr.length>max){
            max=arr.length;
        }
    }
    return max;
};

结果

浅谈算法题:无重复字符的最长子串

高级解法:哈希集合与滑动窗口

为了进一步提升效率,我们采用哈希集合(HashSet)来实时跟踪已出现过的字符,利用其高效的查找能力(平均时间复杂度O(1)),结合滑动窗口策略,设计出一个线性时间复杂度的解法。

算法步骤

  1. 初始化:设定左右指针left=0right=0,用一个哈希集合charSet存储已访问字符,以及一个变量maxLength记录最长子串长度,初始值为0。
  2. 循环遍历:当right<s.length()时执行以下操作:
    • 检查重复:如果s[right]不在charSet中,将其添加进集合,同时更新maxLength为当前窗口大小(即right-left+1)的最大值。
    • 发现重复:若s[right]已在集合中,说明遇到了重复字符,此时应移除charSetleft指向的字符,然后将left指针右移一位,继续探索。
  3. 右指针前进:每完成一次检查,right++,扩大窗口继续搜索。
  4. 返回结果:遍历结束后,maxLength即为所求最长无重复字符子串的长度。

JavaScript实现

function lengthOfLongestSubstring(s) {
    let charSet = new Set();
    let left = 0, right = 0, maxLength = 0;
    
    while (right < s.length) {
        if (!charSet.has(s[right])) {
            charSet.add(s[right]);
            maxLength = Math.max(maxLength, right - left + 1);
            right++;
        } else {
            charSet.delete(s[left]);
            left++;
        }
    }
    return maxLength;
}

结果

浅谈算法题:无重复字符的最长子串

性能分析

此解法的时间复杂度为O(n),因为我们只遍历了一次字符串,每次操作(添加或删除哈希集合中的元素)的平均时间复杂度接近O(1)。空间复杂度也是O(n),因为在最坏情况下,需要存储所有不重复字符。

总结

“无重复字符的最长子串”问题不仅考察了基本的字符串处理能力,更是对动态规划思维与数据结构应用的一次实战演练。通过逐步优化,我们从暴力解法过渡到滑动窗口结合哈希集合的高效策略,不仅实现了算法性能的飞跃,也深刻体会了算法设计中的权衡与优化艺术。掌握这类问题的解决思路,对于准备技术面试、提升编程技能乃至深入理解算法设计原理都有重要意义。希望本文的分析能帮助你更深入地理解这一经典问题,并在未来的算法学习之路上更加得心应手。

转载自:https://juejin.cn/post/7376425155382247464
评论
请登录