提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
个人练习随记,代码参考了题解Link大佬的,在此记录一下自己的理解,以便后续回顾。

一、思路
本题简单来说就是找至多包含两种元素的最长子串,并返回其长度。
这是一个动态的过程,我优先想到的就是经典的双指针滑动窗口,但是在实测以后发现,写出来的双指针的滑动窗口的时间复杂度接近O(n^2),相当于双for的暴力算法了,也可能是我技术的原因。所以参考题解Link大佬的代码后发现新大陆,用四个指针实现窗口的滑动,一个for循环就能搞定而且代码更清晰。
二、个人理解
我认为滑动窗口要明白的三个点:1、窗口区间的定义是什么? 2、for循环代表的是起始位置还是终止位置? 3、 如何实现窗口的滑动?
int result = INT_MIN;// 用以记录最大子序列的长度
int f1 = 0, f2 = 0; // 两个滑动窗口的起始索引位置
int j = 0; // 滑动窗口的终止位置
int t = 0; // 未来第一个篮子的起始位置
本题中:窗口区间肯定是包含两个不同元素的子序列长度,for循环要依次遍历数组的元素,这里代表的应该是终止位置, 由于是利用四个指针,那么就可以用两个指针来记录两个不同元素的起始索引位置,再由两个指针不断的向后移动,直到找到第三个不同元素,依次来更新滑动区间。
for( ; j < fruits.size(); j++){
if(fruits[j] != fruits[f1] && fruits[j] != fruits[f2]){
if (f1 != f2) f1 = t;
f2 = j;
}
if (fruits[t] != fruits[j]) t = j;
result = result > (j - f1 + 1) ? result : (j - f1 + 1);
}
通过for循环使j遍历整个数组,f1,f2作为滑动窗口起始位置,即两个篮子,t作为未来第一个篮子的起始位置。
当遍历到第三种水果类型时,若f1 != f2, 则更新f1 = t ,此时f1就为新窗口的起始位置
当遍历到第二种或第三种水果类型时,则更新f2 = j,这样窗口里面就保证是两种类型的水果
例子:[3,3,3,1,2,1,1,2,3,3]
当j = 0时,f1 = f2 = t = 0,只有result = 0 > (0 - 0 + 1) ? 0 : (0 - 0 + 1),即result = 1
前三轮循环都只会更新result的值,因为并没有发现另外一种类型的水果,此时result = 3
当j = 3时,j -> 1,f1 = f2 = t -> 3, 此时fruits[j] != fruits[f1] != fruits[f2] != fruits[t],说明遇到到第二种不同类型的水果,又因为 f1 = f2 所以并不会更新f1的位置,f2的位置更新为3,t的位置更新为3,滑动窗口的长度result = 4,即[3, 3, 3, 1],其中f1为第一种水果类型的初始位置,也是滑动窗口的初始位置,f2是第二种水果类型的初始位置,t作为未来如果有遇到除3和1外,第三种水果类型的新滑动窗口的初始索引位置。
当j = 4时,j -> 2, f1 = 0 -> 3, f2 = 3 -> 1, t = 3 -> 1,此时遇到了第三种水果类型 2,要更新滑动窗口的区域。
fruits[j] != fruits[f1] != fruits[f2] != fruits[t],f1 != f2,所以更新第一种水果的初始索引位置为t,即f1 = 3 -> t, f2 = 4 -> 2,这样就把滑动窗口两种类型的水果由原来的3,1更新为了1,2,起始位置由原来的0更新为了3,第一种水果类型[3]的起始位置由原来0更新为了3, 第二种水果类型[1]的起始位置由原来的3更新为了4。
滑动窗口以[1,2]为底,一直到j = 8时,才遇到第三种水果类型3,此时才会更新 新滑动窗口的底为[2,3],此时result的值为5,即[1, 2, 1, 1, 2],循环结束后便找到了包含两种不同元素的最长的子序列。
三、代码部分
class Solution {
public:
int totalFruit(vector<int>& fruits) {
if (fruits.size() < 3) return fruits.size();
int result = INT_MIN;
int f1 = 0, f2 = 0; // 两个滑动窗口的起始索引位置
int j = 0; // 滑动窗口的终止位置
int t = 0; // 未来第一个篮子的起始位置
for( ; j < fruits.size(); j++){
if(fruits[j] != fruits[f1] && fruits[j] != fruits[f2]){
if (f1 != f2) f1 = t;
f2 = j;
}
if (fruits[t] != fruits[j]) t = j;
result = result > (j - f1 + 1) ? result : (j - f1 + 1);
}
return result;
}
};
总结
滑动窗口果然是需要一个动态的想法,区间的定义和for循环代表的位置根据题目也各不相同,具体问题还需具体分析,
740





