水果成篮

LeetCode 904

水果成篮

在一排树中,第i棵树产生tree[i]型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:

  1. 把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
  2. 移动到当前树右侧的下一棵树。如果右边没有树,就停下来。

请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1 ,然后执行步骤 2,依此类推,直至停止。

你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。
用这个程序你能收集的水果总量是多少?

解法1:两层for循环

解题思路:

从一个起点开始,遍历后面的元素,如果有一个元素既不等于起点又不等于前面不等于起点的元素,我们就停止遍历

代码如下:

class Solution {
    public int totalFruit(int[] tree) {
        int res = 0;
        int i, j;
        for(i=0; i<tree.length; i++)
        {
            int pre=0; //用pre来保存前面一个元素跟起点的差
            for(j=i+1; j<tree.length; j++)
            {
                if(tree[j]-tree[i]!=0 && tree[j]-tree[i]!=pre)
                {
                    if(pre==0) //如果前面一个元素跟起点相同
                        pre=tree[j]-tree[i]; //则当前元素为第二个元素
                    else 
                    //如果当前元素跟起点的差不等于前面一个元素跟起点的差,说明两者不相等,当前元素为第三个元素,退出循环
                        break;
                }
            
            }
            res = Math.max(res,j-i); //保存结果
        }
        return res;
    }
}

时间复杂度为O(N^2)

空间复杂度为O(1)

解法2:按块扫描

解题思路:

我们把一段连续的数字称为块,我们用一个block数组来保存每一个块的首地址,如

tree = [(1,1),(2,2),(1,1),(2),(3,3,3)] //括号表示一块连续数字
block = [0,2,4,6,7] //分别代表每一块的首地址

我们把一个块里面的值称为块元素

那么我们的任务就是找到由两种块元素组成的最大长度的块,而且这两种类型元素的块必须是相邻的

注意:我们遍历block数组时,当找完两种元素的块,只要从前面一个块重新开始找就可以了,
如果从前面两个块开始找,那就没啥意义了,因为已经存在两种元素了,没办法继续往下找

代码如下:

class Solution {
    public int totalFruit(int[] tree) {
        List<Integer> blockLefts = new ArrayList();

    
        for (int i = 0; i < tree.length; ++i)//存放每一块的首地址
            if (i == 0 || tree[i-1] != tree[i])
                blockLefts.add(i);

        // 代表块的结尾
        blockLefts.add(tree.length);

        int ans = 0, i = 0;
            search: while (true) {
                //用来保存每一个块元素
                Set<Integer> types = new HashSet();
                int weight = 0; //组合块的长度

                遍历块
                for (int j = i; j < blockLefts.size() - 1; ++j) {
                    // 加入块元素
                    types.add(tree[blockLefts.get(j)]);
                    weight += blockLefts.get(j+1) - blockLefts.get(j);
                    //下一个块的首地址减去当其块的首地址等于当前块的长度
                    // 如果有三种块元素
                    if (types.size() >= 3) {
                        i = j - 1; //返回到上一个块开始遍历
                        continue search;
                    }

                    //保存组合块的最大长度
                    ans = Math.max(ans, weight);
                }

                break;
            }

            return ans;
        }
    }

时间复杂度为O(N)

空间复杂度为O(N)

解法3:滑动窗口

解题思路:

下标:[0,1,2,3,4,5,6,7,8,9]

数值:[1,1,6,5,6,6,1,1,1,1]

从左往右先找到2种树,即(1, 6)
当end下标为3时,遇到了第3种树(5),而第3种树(5)左边的树是6,将2种树的状态更新为(6,5)
以此类推,当下标为6时,遇到了第3种树(1),而第3种树(1)左边的树是6,将2种树的状态更新为(6,1)

即每次2种树的状态更新为(第3种树的左边的树, 第3种树)

当理解上述思路后,就开始计数由这2种树所构成的连续子数组。

(1, 6) -> [1, 1, 6] ->连续子数组长度3

(6, 5) -> [6, 5, 6, 6] ->连续子数组长度为4

(6, 1) -> [6, 6, 1, 1, 1, 1] ->连续子数组长度为6

取连续子数组的最长长度作为返回结果,也就是6

代码如下:

class Solution {
    public int totalFruit(int[] tree) {
        int res = 0, len = tree.length;
        int one = tree[0], two, begin = 0, end = 1; 
        //2种树的状态(one, two), one初始化为tree数组的第1个元素
        while (end < len && tree[end] == one)   
        //寻找two的初始值,以构成初始(one, two)
            ++end;
        if (end >= len-1) return len; 
        //若整个数组的元素都由初始的(one, two)所构成,则直接返回数组长度
        two = tree[end++];  //构成初始的(one, two)
        for (; end < len; ++end) {
            if (one != tree[end] && two != tree[end]) { 
                //遇到了第3种树
                res = Math.max(res, end - begin);   
                //更新最终返回结果
                one = tree[end - 1];    
                //(one, two)更新为(第3种树的左边的树, 第3种树)
                two = tree[end];
                begin = end - 1;    
                //更新由当前(one, two)所构成的连续子数组的左边界
                while (tree[begin - 1] == one)   
                //向左寻找由one构成的连续子数组
                    --begin;
            }
        }
        return Math.max(res, end - begin);
    }
}

时间复杂度为O(N)
空间复杂度为O(1)
解法3来源:

作者:gfu
链接:https://leetcode-cn.com/problems/fruit-into-baskets/solution/java-hua-dong-chuang-kou-5ms-beat-100-by-gfu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值