904. 水果成篮

904. 水果成篮

题目链接

分析

说白了,很简单,就是输出,仅包含两个不同数值的最大子数组的长度,这个其实跟 209. 长度最小的子数组 的逻辑是一样的,
209. 长度最小的子数组 中,快慢指针的移动条件是:

解题

这个题我花的时间有点久,其实问题不在于分析思路,思路其实就是如何解题(双指针/移动窗口)具体一点就是什么时候移动快慢指针,花的时间久主要在于细节太多了,比如如何保存篮子里的水果,移动慢指针的时候,什么时候才能把篮子空出来(需要计数)。那就用两个数组,一个数组保存种类,一个数组保存个数。
首先要定位 slow 和 fast 两个指针的定义是什么,然后按照这个定义来初始化条件,先根据 fast 计算长度,再 +1,并判断长度。
要填充两个篮子,我的思路是默认空出来第二个篮子,然后是记录这两种数字出现的次数,然后在移动满指针的时候把篮子空出来,并在需要移动的时候移动。

public int totalFruit(int[] fruits) {
    if(fruits.length<=2){
        return fruits.length;
    }
    int nowLength = -1;
    // 用一个数组表示篮子里的水果的种类
    int[] packages = new int[2];
    packages[0]=fruits[0];
    // 还没放水果就设置为-1
    // 不能设置为null,只能给个-1 表示,fruits的元素都是>0的,不会冲突
    packages[1]=-1;
    // 再用一个数组表示篮子里的水果的数量,根据索引跟水果种类数组对应
    int[] fruitCount = new int[2];
    fruitCount[0]=1;
    // 还没放水果就0
    fruitCount[1]=0;
    // slow 指的是,采摘的第一棵树
    // fast 指的是准备采摘的下一个树
    int slow=0,fast = 1;
    for(;;){
            if(fruits[fast]== packages[0] || fruits[fast]== packages[1]){
            // 要摘的水果篮子里已经有了,直接放进去
            //更新长度
            int length =  fast-slow+1;
            if(length > nowLength){
                nowLength = length;
            }
            //记录篮子里的水果的个数
            if(fruits[fast]== packages[0] ){
                fruitCount[0]+=1;
            }
            if(fruits[fast]== packages[1] ){
                fruitCount[1]+=1;
            }
            fast++;;
            if(fast>fruits.length-1){
                break;
            }
        }else if(packages[1] == -1){
            // 要摘的水果篮子里没有,那有没有空蓝子,
            // 为了方便,我们只检查下标为1的位置,后面移动慢指针得时候,我们也会把下标为1的篮子空出来
            packages[1]=fruits[fast];
            //记录篮子里的水果的个数
            fruitCount[1]=1;
            //更新长度
            int length =  fast-slow+1;
            if(length > nowLength){
                nowLength = length;
            }
            fast++;
            if(fast>fruits.length-1){
                break;
            }
        }else{
            // 要摘的水果篮子里没有,而且没有空蓝子,那就只能把篮子里的水果往外拿了
            if(fruits[slow]== packages[0] ){
                // 如果是第1个篮子里的水果,那就减少第1个篮子的水果的个数
                fruitCount[0]-=1;
                if(fruitCount[0]==0){
                    // 如果水果个数为0了,那就把篮子清空,同时把第2个篮子的数据移动到第1个篮子,方便后续判断。
                    packages[0]=packages[1];
                    packages[1]=-1;
                    fruitCount[0]=fruitCount[1];
                    fruitCount[1]=0;
                }
            }
            if(fruits[slow]== packages[1] ){
                // 如果是第2个篮子里的水果,那就减少第2个篮子的水果的个数
                fruitCount[1]-=1;
                if(fruitCount[1]==0){
                    // 如果水果个数为0了,那就把篮子清空,
                    packages[1]=-1;
                }
            }
            slow++;
        }
    }
    return nowLength;
}

同时,我们还有优化的空间,就是我们有没有必要保存篮子里的水果的个数,在移动慢指针的时候,水果的个数需要加减,很麻烦,我们可以记录篮子里的水果的最大索引,这样只用比较索引大小即可判断是不是该空出水果篮子了,比较方便。然后我就发现,我都记录了水果的索引了,那也就没有必要记录水果 packages 数组了,有了索引,直接通过水果数组加上索引 fruits[index] 来访问即可,所以又可以节约代码了。

public int totalFruit(int[] fruits) {
    if(fruits.length<=2){
        return fruits.length;
    }
    int nowLength = -1;
    int slow=0,fast=1;
    int[] packageIndex = new int[]{0,-1};
    for(;fast<fruits.length;){
        // 通过最大索引判断篮子里是否已经有水果
        // 如果没有水果,或者有水果,就是fast指向的这个水果,那就可以直接放入,同时更新水果的最大索引
        if(packageIndex[0]==-1||fruits[fast]==fruits[packageIndex[0]]){
            packageIndex[0] = fast;
            int length = fast-slow+1;
            if(nowLength < length){
                nowLength = length;
            }
            fast++;
        }else if(packageIndex[1]==-1||fruits[fast]==fruits[packageIndex[1]]){
            packageIndex[1] = fast;
            int length = fast-slow+1;
            if(nowLength < length){
                nowLength = length;
            }
            fast++;
        } else {
            // 如果两个篮子里都不能放,则开始收缩
            // 只需要比较最大索引就可以直接判定是否清空篮子
            if(slow==packageIndex[0]){
                packageIndex[0]=-1;
            }
            if(slow==packageIndex[1]){
                packageIndex[1]=-1;
            }
            slow++;
        }
    }
    return nowLength;
}

官方题解倒是简单,直接用一个 HashMap 来表示水果篮子,key 就是水果品种,value 就是水果的个数,我老觉得这样有点讨巧,我最终还是喜欢用数组来解决问题,

相关题

26. 删除有序数组中的重复项
27. 移除元素
283. 移动零
844. 比较含退格的字符串
977. 有序数组的平方
滑动窗口
209. 长度最小的子数组
76. 最小覆盖子串