滑动窗口题目:水果成篮
文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:水果成篮
出处:904. 水果成篮
难度
5 级
题目描述
要求
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits\texttt{fruits}fruits 表示,其中 fruits[i]\texttt{fruits[i]}fruits[i] 是第 i\texttt{i}i 棵树上的水果种类。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求收集水果:
- 你只有两个篮子,并且每个篮子只能装单一类型的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始收集,向右移动的过程中,你必须从每棵树(包括开始收集的树)上恰好摘一个水果。收集的水果应当符合一个篮子中的水果类型。
- 一旦你走到某棵树前,但水果不符合任何篮子中的水果类型,那么就必须停止收集。
给定一个整数数组 fruits\texttt{fruits}fruits,返回你可以收集的水果的最大数目。
示例
示例 1:
输入:fruits=[1,2,1]\texttt{fruits = [1,2,1]}fruits = [1,2,1]
输出:3\texttt{3}3
解释:可以收集全部 3\texttt{3}3 棵树。
示例 2:
输入:fruits=[0,1,2,2]\texttt{fruits = [0,1,2,2]}fruits = [0,1,2,2]
输出:3\texttt{3}3
解释:可以收集 [1,2,2]\texttt{[1,2,2]}[1,2,2] 这三棵树。
如果从第一棵树开始收集,则只能收集 [0,1]\texttt{[0,1]}[0,1] 这两棵树。
示例 3:
输入:fruits=[1,2,3,2,2]\texttt{fruits = [1,2,3,2,2]}fruits = [1,2,3,2,2]
输出:4\texttt{4}4
解释:可以收集 [2,3,2,2]\texttt{[2,3,2,2]}[2,3,2,2] 这四棵树。
如果从第一棵树开始收集,则只能收集 [1,2]\texttt{[1,2]}[1,2] 这两棵树。
数据范围
- 1≤fruits.length≤105\texttt{1} \le \texttt{fruits.length} \le \texttt{10}^\texttt{5}1≤fruits.length≤105
- 0≤fruits[i]<fruits.length\texttt{0} \le \texttt{fruits[i]} < \texttt{fruits.length}0≤fruits[i]<fruits.length
解法
思路和算法
根据收集水果的要求,收集的水果必须是数组 fruits\textit{fruits}fruits 中的一个子数组,且子数组中至多包含 222 个不同元素。由于每棵树必须摘一个水果,因此收集的水果数目等于子数组的长度。为了得到收集的水果的最大数目,应得到符合要求的最长子数组。
根据上述分析,问题转化成在数组 fruits\textit{fruits}fruits 中寻找至多包含 222 个不同元素的最长子数组。
考虑数组 fruits\textit{fruits}fruits 的两个不同下标 end1\textit{end}_1end1 和 end2\textit{end}_2end2,其中 end1<end2\textit{end}_1 < \textit{end}_2end1<end2,分别以这两个下标作为结束下标,寻找至多包含 222 个不同元素的最长子数组,将这两个最长子数组的开始下标分别记为 start1\textit{start}_1start1 和 start2\textit{start}_2start2,则必有 start1≤start2\textit{start}_1 \le \textit{start}_2start1≤start2(否则对于任意 start2≤start3<start1\textit{start}_2 \le \textit{start}_3 < \textit{start}_1start2≤start3<start1,下标范围 [start3,end1][\textit{start}_3, \textit{end}_1][start3,end1] 的子数组至多包含 222 个不同元素,且长度大于下标范围 [start1,end1][\textit{start}_1, \textit{end}_1][start1,end1] 的子数组)。
因此,可以使用变长滑动窗口寻找数组 fruits\textit{fruits}fruits 中的至多包含 222 个不同元素的最长子数组。用 [start,end][\textit{start}, \textit{end}][start,end] 表示滑动窗口,初始时 start=end=0\textit{start} = \textit{end} = 0start=end=0。将滑动窗口的右端点 end\textit{end}end 向右移动,移动过程中维护滑动窗口的左端点 start\textit{start}start,确保滑动窗口中至多包含 222 个不同元素。
使用哈希表记录滑动窗口中的每个元素的出现次数。对于每个右端点 end\textit{end}end,执行如下操作。
-
将 fruits[end]\textit{fruits}[\textit{end}]fruits[end] 在哈希表中的次数加 111。
-
如果哈希表中的元素个数大于 222,则将 fruits[start]\textit{fruits}[\textit{start}]fruits[start] 在哈希表中的次数减 111,如果 fruits[start]\textit{fruits}[\textit{start}]fruits[start] 在哈希表中的次数变成 000 则将 fruits[start]\textit{fruits}[\textit{start}]fruits[start] 从哈希表中删除,然后将 start\textit{start}start 向右移动一位,重复该操作直到哈希表中的元素个数小于等于 222。
-
当前滑动窗口 [start,end][\textit{start}, \textit{end}][start,end] 中的子数组为至多包含 222 个不同元素的子数组,其长度为 end−start+1\textit{end} - \textit{start} + 1end−start+1,使用当前子数组的长度更新子数组的最大长度。
遍历结束之后,可以得到数组 fruits\textit{fruits}fruits 中的至多包含 222 个不同元素的子数组的最大长度,即收集的水果的最大数目。
实现方面,由于数组 fruits\textit{fruits}fruits 中的所有元素都是小于数组长度的非负整数,因此可以创建一个与数组 fruits\textit{fruits}fruits 等长的数组 counts\textit{counts}counts 代替哈希表,当数组 counts\textit{counts}counts 中的一个元素值从 000 变成 111 时哈希表中的元素个数增加 111 个,当数组 counts\textit{counts}counts 中的一个元素值从 111 变成 000 时哈希表中的元素个数减少 111 个。
代码
class Solution {public int totalFruit(int[] fruits) {int length = fruits.length;int[] counts = new int[length];int maxFruits = 0;int baskets = 0;int start = 0, end = 0;while (end < length) {int type = fruits[end];counts[type]++;if (counts[type] == 1) {baskets++;}while (baskets > 2) {int prev = fruits[start];counts[prev]--;if (counts[prev] == 0) {baskets--;}start++;}maxFruits = Math.max(maxFruits, end - start + 1);end++;}return maxFruits;}
}
复杂度分析
-
时间复杂度:O(n)O(n)O(n),其中 nnn 是数组 fruits\textit{fruits}fruits 的长度。滑动窗口的左右端点最多各遍历数组 fruits\textit{fruits}fruits 一次。
-
空间复杂度:O(n)O(n)O(n),其中 nnn 是数组 fruits\textit{fruits}fruits 的长度。空间复杂度主要取决于哈希表,哈希表中的不同元素个数不超过数组 fruits\textit{fruits}fruits 的长度。