今日一题,阿du带大家体验下滑动窗口算法思想的运用,上例题:力扣(LeetCode)无重复字符的最长子串:给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。示例:输入s = "abcabcaa" ,因为不重复字符的最长子串为“abc”,所以结果为3。
初看到这道题阿du急急忙忙就开始下手写,双层for循环直接实现,但真实运行起来,时间复杂度为O()很耗时,重要的是也满足不了面试官的要求呀(捂脸)
public int lengthOfLongestSubstring(String s) {
//记录最大值
int max =0;
if(s.length()==1){
return 1;
}else if(s.length()==0){
return max;
}
//转换为字符数组
char[] cc = s.toCharArray();
for(int i=0;i<cc.length;i++){
List<Character> list = new ArrayList();
list.add(cc[i]);
for(int j=i+1;j<cc.length;j++){
if(list.contains(cc[j])){
break;
}else{
list.add(cc[j]);
}
}
//无重复字符的情况更新max
max=max>list.size()?max:list.size();
}
return max;
}
那么有没有更加优化的算法呢,能够让时间复杂度保持在O(n)水平,这里阿du用到了滑动窗口算法思想,何谓滑动窗口, 简单点说就像是一个窗子,你可以通过左边框和右边框来拉着滑动它,从而可以使它包含不同的区域,需要注意所包含的元素区域都是连续排列的,我们这里就可以用左右指针来做标识代替左右边框在给定的字符串中滑动来找到满足要求的最长子串。
public int longestSubstring(String s) {
//字符串长度
int length = s.length();
//记录滑动窗口的最大值
int ans = 0;
Map<Character,Integer> map = new HashMap<>();
//start、end滑动窗口的左右边框
for(int start=0,end=0;end<length;end++){
//有重复元素时移动窗口左边框
if(map.containsKey(s.charAt(end))){
//锁定滑动窗口的左边框(保证窗口左边框是从左向右依次移动)
start = Math.max(map.get(s.charAt(end))+1,start);
}
//更新最长子串的长度
ans = Math.max(ans,end-start+1);
map.put(s.charAt(end),end);
}
return ans;
}
小总结: 滑动窗口算法比较重要的就是左右边框的移动,只要把左右边框移动规则搞明白了,上手写代码就很快了,另外窗口内包含的元素都是连续的,本题中利用的是动态的滑动窗口也就是窗口的区域是动态变化的,还有一种是固定的窗口滑动,也就是在移动过程中窗口大小始终是n固定不变的比如求n个连续元素的最小值等。滑动窗口算法可以将嵌套的循环问题,转换为单循环问题,降低时间复杂度,下次遇到类似求连续最大最小值 无重复的最长子串等题目注意运用滑动窗口算法哦。
好了 下次见阿du啦!