力扣904. 水果成篮

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

个人练习随记,代码参考了题解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循环代表的位置根据题目也各不相同,具体问题还需具体分析,

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值