浅谈算法题:无重复字符的最长子串
在算法学习的旅程中,解决字符串处理问题是不可或缺的一环。其中,“无重复字符的最长子串”作为一道经典问题,不仅考验着程序员对字符串操作的基本功,还涉及到了动态规划、滑动窗口等高级算法思想的巧妙运用。本文将深入浅出地探讨这个问题,从基本思路出发,逐步优化解法,并引入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)),结合滑动窗口策略,设计出一个线性时间复杂度的解法。
算法步骤
- 初始化:设定左右指针
left=0
,right=0
,用一个哈希集合charSet
存储已访问字符,以及一个变量maxLength
记录最长子串长度,初始值为0。 - 循环遍历:当
right<s.length()
时执行以下操作:- 检查重复:如果
s[right]
不在charSet
中,将其添加进集合,同时更新maxLength
为当前窗口大小(即right-left+1
)的最大值。 - 发现重复:若
s[right]
已在集合中,说明遇到了重复字符,此时应移除charSet
中left
指向的字符,然后将left
指针右移一位,继续探索。
- 检查重复:如果
- 右指针前进:每完成一次检查,
right++
,扩大窗口继续搜索。 - 返回结果:遍历结束后,
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