前言
这个星期做的都是有关双指针和滑动窗口的有关leecode题。 同样有点小感悟,这里小小总结一下双指针中的滑动窗口部分。
一、一个小总结
对于给定字符串求其某个符合一定条件的子串的问题,最简单的方法就是暴力的两层循环,它的时间复杂度至少是O(N2)。 一般条件下,这样的复杂的往往都会因为超时而“over”。
相对来说,如果能对暴力的两个指针的移动进行优化(或者裁剪,避免很多已经不可能的指针移动),很多题目都可以使用滑动窗口的方式来解决。
滑动窗口说起来其实也很简单,它主要是利用两个指针在一维数组中进行分别滑动,从而进行搜索可行解的一种算法。一般来说,两个指针的滑动方向是一致的。
相比于利用两个指针进行O(N2)的暴力算法(两层循环),这种算法大大减少了复杂度(单从指针移动的角度来看,这种复杂度为O(N))。 在我看来,滑动窗口和直接的暴力求解的根本不同点就在于前者在每次指针移动的时候都进行了逻辑判断,把不必要的条件都裁剪了,滑动的窗口里留下的是可行解(也有可能没有解)。
一般来说,left指针不会在right指针已经移到end端,才向右滑动;相反的,left会在窗口中的值已经满足条件(或者不满足条件,视具体的实现而定)就可以进行向右移动。移动后的在窗口左边的元素不会再被考虑(如果窗口是往右滑动的话),因为包含滑动窗口左边元素的所有最优情况已经都被考虑过了。
就这样,一步一步滑动,一步一步进行窗口内元素检测,最终可以确定最优的解。 从某种程度上来说,这也是一种搜索。和动态规划有点像,一种自带裁剪的搜索算法。
以上的内容都是抽象的总结概括,如果对滑动窗口不是很了解,可能觉得我总结的都是啥啥玩意。 这里还是建议在其他地方了解、实践一波。 ( * ^ ▽ ^ * )
二、一个小模板
上面说的是对滑动窗口的一些总结和介绍部分,这里原来想结合leecode的题来点实现代码来着。 但是发现自己写的几题关于滑动窗口的题都有点信马由缰、不成体系,有点小乱,这里就不再献丑了。^ _ ^|||
这里贴个关于滑动窗口的模板,看起来还挺不错的。
/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
...
/*** debug 输出的位置 ***/
printf("window: [%d, %d)\n", left, right);
/********************/
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
以上模板来自【1】。 模板的作用是在理解的基础上加快解题速度,事先还是要理解好原理的。