leetcode 904 水果成篮

 题目描述:代码随想录-数组-5-滑动窗口

中等

904. 水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

思路:

1、直接暴力解

类似双指针的滑动窗口,定义左指针(慢,i)和右指针(快,j),定义一个sum 是记录这次能装多少个水果,定义type1,type2记录两种水果的种类。慢指针指向装的第一个水果(顺便定义了type1 = fruits[i]),定义 k 代表第二种水果第一次出现的位置(顺便定义了type2 = fruits[k]),快指针从0开始循环,往右移动的过程中,每次移动需要判断 fruits[j] 是否等于type1或type2中的一个,如果等于,sum++ ;如果不等于,就需要重置,即挪动 i 指针,第一种方法是直接 i++,sum--, 太费时间;第二种方法是直接把 i 移到 第二种水果第一次出现的位置即 k ,此时 sum也需要更新为 sum - (k - i) 。应该就差不多了。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        //符合只有两个不同数的,最长子序列,滑动窗口 15:22 (5月底没想完)
        //7.2 继续想20:00开始,想到21.30也没AC 纯傻逼;
        //7.4下午3.10又开始debug,暴力解应该没问题了,就是超出时间限制,o(n^2)
        //7.4 想更新i-----16.44通过了,注意,滑动窗口i跳了的话,Sum也得跳
        int i = 0;
        int sum = 1;
        int type1 = -1; 
        int type2 = -1;
        int result = 0;

        //边界情况,输入[0]
        if(fruits.size() == 1) return 1;
        //快指针在外循环
        for (int j = 1; j < fruits.size(); j++){
            //定义type1
            type1 = fruits[i];

            // if((fruits[j] != type1) && (type2 == -1)) type2 = fruits[j];  //初值赋的不对,这样会跳过中间某些数如 [3,3,3,1,2,1,1,2,3,3,4]

            //定义type2 出现在 k处
            int k = i;  //k的位置也很重要,必须在这个外面
            if((fruits[j] != type1) && (type2 == -1)){
                for(;k<=j;k++){
                    if(fruits[k]!=fruits[i]){
                        type2 = fruits[k];
                        break;
                    }
                }    
            }


            if((fruits[j] == type1) || (fruits[j] == type2)){
                sum++;
                if(sum > result) {
                    result = sum; // result = j - i + 1; //都可以
                    //cout<<"result (in j = "<<j<<" ) :"<<reuslt<<endl;
                }
            }else{
                // 重置

                // way1 -- 暴力解 i每次只挪一下,way1在leetcode会超出时间
                // i++;
                // sum--;  //一定是sum--!!!因为i往右边移的同时,Sum-1即可

                //way2 -- 滑动窗口 想直接跳i
                sum = sum - (k - i);  //sum得在i前更新
                i = k;
                
                j--; // 一定要!!!不论way1还是way2都要保证j不动,所以先减,再下一次循环再加
                type2 = -1;

            }
        }

        return result;
    }
};

白板输出:

class Solution904 {
public:
    int totalFruit(vector<int>& fruits) {
        //符合只有两个不同数的,最长子序列,滑动窗口 15:22 (5月底没想完)
        //7.2 继续想20:00开始

        int i = 0;
        int sum = 1;
        int type1 = -1;
        int type2 = -1;
        int result = 0;
        int k = -1;
        //边界情况,输入[0]
        if(fruits.size() == 1) return 1;

         //快指针在外循环
        for (int j = 1; j < fruits.size(); j++){
            cout<<"j: "<<j<<endl;
            //定义type1
            type1 = fruits[i];
            //if((fruits[j] != type1)&& (type2 == -1)) type2 = fruits[j];//初值赋的不对,这样会跳过中间某些数如 [3,3,3,1,2,1,1,2,3,3,4]

            //定义type2 出现在 k处

            if((fruits[j] != type1) && (type2 == -1)){
                k = i;
                for(;k<=j;k++){
                    if(fruits[k] != fruits[i]){
                        type2 = fruits[k];
                        break;
                    }
                }
            }
            cout << "type2出现的位置k :" << k <<endl;

            //如果j位置的水果与type1 type2相同,sum++
            if((fruits[j] == type1)||(fruits[j] == type2)){
                sum++;
                if(sum > result) {
                    result = sum;    //way1
                    //result = j - i + 1;
                    cout<<"type1,2: " << type1 << " " <<type2<<endl;
                    cout<<"result (in i = " << i <<" j = "<<j<<" ) :"<<result<<endl;
                }
            }else{

//                // way1
//                cout << "i = " << i << endl;
//                i++;
//                j--; // 一定要!!!!!
//                sum--;

                // way2 直接跳i, i更新,sum也得更新
                // i = j - 1;//绝对不对
                sum = sum - (k - i);  //sum得在i前更新
                i = k;
                j--;


                type2 = -1;
                cout << "i = " << i << endl;
                cout<<"重置"<<endl;
            }
        }

        return result;
    }
};

int main(){
    
    Solution904 sol;

    int arr1[3] = {1,2,1};
    vector<int> fruits1 ;
    for(int i = 0;i<3;i++){
        fruits1.push_back(arr1[i]);
        cout<<fruits1[i]<<"";
    }
    cout << endl;
    cout << "示例输出:" <<sol.totalFruit(fruits1) <<endl;
    cout << endl;

    int arr2[4] = {0,1,2,2};
    vector<int> fruits2 ;
    for(int i = 0;i<4;i++){
        fruits2.push_back(arr2[i]);
        cout << fruits2[i]<<" ";
    }
    cout << endl;
    cout << "示例输出:" <<sol.totalFruit(fruits2) <<endl;
    cout << endl;

    int arr41[11] = {3,3,3,1,2,1,1,2,3,3,4};
    vector<int> fruits41 ;
    for(int i = 0;i<11;i++){
        fruits41.push_back(arr41[i]);
        cout << fruits41[i]<<" ";
    }
    cout << endl;
    cout << "示例输出:" <<sol.totalFruit(fruits41) <<endl;
    cout << endl;

    int arr68[1] = {0};
    vector<int> fruits68 ;
    fruits68.push_back(arr68[0]);
    cout << endl;
    cout << "示例输出:" <<sol.totalFruit(fruits68) <<endl;
    cout << endl;

    int arr3[2] = {0,1};
    vector<int> fruits3 ;
    for(int i = 0;i<2;i++){
        fruits3.push_back(arr3[i]);
        cout << fruits3[i]<<" ";
    }
    cout << endl;
    cout << "示例输出:" <<sol.totalFruit(fruits3) <<endl;
    cout << endl;

    int arr69[7] = {0,1,6,6,4,4,6};
    vector<int> fruits69 ;
    for(int i = 0;i<7;i++){
        fruits69.push_back(arr69[i]);
        cout << fruits69[i]<<" ";
    }
    cout << endl;
    cout << "示例输出:" <<sol.totalFruit(fruits69) <<endl;
    cout << endl;


    int arr80[8] = {4,1,1,1,3,1,7,5};
    vector<int> fruits80 ;
    for(int i = 0;i<8;i++){
        fruits80.push_back(arr80[i]);
        cout << fruits80[i]<<" ";
    }
    cout << endl;
    cout << "示例输出:" <<sol.totalFruit(fruits80) <<endl;
    cout << endl;

   
    return 0;
}

2、其他解法

来自leetcode评论区

(1)上面暴力解的改进版,在循环 j 时就顺便把第二种水果第一次出现的位置记录下来

力扣 C++ 双指针,可以用一个单独指针指向left下一次跳转的位置,也可以先left=right-1,再进行回退

class Solution {
    public:
    int totalFruit(vector<int>& fruits) {
    int left = 0, right = 0; // 双指针法,自己想到的!
    int firstSort = -1, secondSort = -1;
    int maxCount = 0; // 可以收集的最大数目
    int thirdBefore = 0; // thirdBefore指向第三种果树的前一种果树的第一棵

    firstSort = fruits[left]; 
    for (right = 0; right < fruits.size(); right++) {
        if (fruits[right] != firstSort && secondSort == -1) {
            secondSort = fruits[right];
        }
        if (fruits[right] == firstSort || fruits[right] == secondSort) {
            maxCount = right - left + 1 > maxCount ? right - left + 1 : maxCount;
        } else {
            left = thirdBefore; // 移动慢指针
            firstSort = fruits[left];
            secondSort = fruits[right];
        }
        if (right > 0 && fruits[right] != fruits[right-1]) {
            thirdBefore = right;
        }
    }
    return maxCount;
}

作者:meirikele
链接:https://leetcode.cn/problems/fruit-into-baskets/solution/by-meirikele-qpqu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 (2)这道题是最大覆盖字串,和最小覆盖子串的区别

力扣

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值