0904-水果成篮

Raphael Liu Lv10

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

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

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

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

示例 1:

**输入:** fruits = [ _ **1,2,1**_ ]
**输出:** 3
**解释:** 可以采摘全部 3 棵树。

示例 2:

**输入:** fruits = [0, _ **1,2,2**_ ]
**输出:** 3
**解释:** 可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:

**输入:** fruits = [1, _ **2,3,2,2**_ ]
**输出:** 4
**解释:** 可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:

**输入:** fruits = [3,3,3, _ **1,2,1,1,2**_ ,3,3,4]
**输出:** 5
**解释:** 可以采摘 [1,2,1,1,2] 这五棵树。

提示:

  • 1 <= fruits.length <= 105
  • 0 <= fruits[i] < fruits.length

方法一:滑动窗口

思路与算法

我们可以使用滑动窗口解决本题,left 和 right 分别表示满足要求的窗口的左右边界,同时我们使用哈希表存储这个窗口内的数以及出现的次数。

我们每次将 right 移动一个位置,并将 fruits}[\textit{right}] 加入哈希表。如果此时哈希表不满足要求(即哈希表中出现超过两个键值对),那么我们需要不断移动 left,并将 fruits}[\textit{left}] 从哈希表中移除,直到哈希表满足要求为止。

需要注意的是,将 fruits}[\textit{left}] 从哈希表中移除后,如果 fruits}[\textit{left}] 在哈希表中的出现次数减少为 0,需要将对应的键值对从哈希表中移除。

代码

[sol1-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int n = fruits.size();
unordered_map<int, int> cnt;

int left = 0, ans = 0;
for (int right = 0; right < n; ++right) {
++cnt[fruits[right]];
while (cnt.size() > 2) {
auto it = cnt.find(fruits[left]);
--it->second;
if (it->second == 0) {
cnt.erase(it);
}
++left;
}
ans = max(ans, right - left + 1);
}
return ans;
}
};
[sol1-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int totalFruit(int[] fruits) {
int n = fruits.length;
Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();

int left = 0, ans = 0;
for (int right = 0; right < n; ++right) {
cnt.put(fruits[right], cnt.getOrDefault(fruits[right], 0) + 1);
while (cnt.size() > 2) {
cnt.put(fruits[left], cnt.get(fruits[left]) - 1);
if (cnt.get(fruits[left]) == 0) {
cnt.remove(fruits[left]);
}
++left;
}
ans = Math.max(ans, right - left + 1);
}
return ans;
}
}
[sol1-C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Solution {
public int TotalFruit(int[] fruits) {
int n = fruits.Length;
IDictionary<int, int> cnt = new Dictionary<int, int>();

int left = 0, ans = 0;
for (int right = 0; right < n; ++right) {
cnt.TryAdd(fruits[right], 0);
++cnt[fruits[right]];
while (cnt.Count > 2) {
--cnt[fruits[left]];
if (cnt[fruits[left]] == 0) {
cnt.Remove(fruits[left]);
}
++left;
}
ans = Math.Max(ans, right - left + 1);
}
return ans;
}
}
[sol1-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
cnt = Counter()

left = ans = 0
for right, x in enumerate(fruits):
cnt[x] += 1
while len(cnt) > 2:
cnt[fruits[left]] -= 1
if cnt[fruits[left]] == 0:
cnt.pop(fruits[left])
left += 1
ans = max(ans, right - left + 1)

return ans
[sol1-JavaScript]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var totalFruit = function(fruits) {
const n = fruits.length;
const cnt = new Map();

let left = 0, ans = 0;
for (let right = 0; right < n; ++right) {
cnt.set(fruits[right], (cnt.get(fruits[right]) || 0) + 1);
while (cnt.size > 2) {
cnt.set(fruits[left], cnt.get(fruits[left]) - 1);
if (cnt.get(fruits[left]) == 0) {
cnt.delete(fruits[left]);
}
++left;
}
ans = Math.max(ans, right - left + 1);
}
return ans;
};
[sol1-C]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#define MAX(a, b) ((a) > (b) ? (a) : (b))

typedef struct {
int key;
int val;
UT_hash_handle hh;
} HashItem;

HashItem *hashFindItem(HashItem **obj, int key) {
HashItem *pEntry = NULL;
HASH_FIND_INT(*obj, &key, pEntry);
return pEntry;
}

bool hashAddItem(HashItem **obj, int key, int val) {
if (hashFindItem(obj, key)) {
return false;
}
HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
pEntry->key = key;
pEntry->val = val;
HASH_ADD_INT(*obj, key, pEntry);
return true;
}

bool hashSetItem(HashItem **obj, int key, int val) {
HashItem *pEntry = hashFindItem(obj, key);
if (!pEntry) {
hashAddItem(obj, key, val);
} else {
pEntry->val = val;
}
return true;
}

int hashGetItem(HashItem **obj, int key, int defaultVal) {
HashItem *pEntry = hashFindItem(obj, key);
if (!pEntry) {
return defaultVal;
}
return pEntry->val;
}

void hashFree(HashItem **obj) {
HashItem *curr = NULL, *tmp = NULL;
HASH_ITER(hh, *obj, curr, tmp) {
HASH_DEL(*obj, curr);
free(curr);
}
}

int totalFruit(int* fruits, int fruitsSize){
HashItem *cnt = NULL;
int left = 0, ans = 0;
for (int right = 0; right < fruitsSize; ++right) {
hashSetItem(&cnt, fruits[right], hashGetItem(&cnt, fruits[right], 0) + 1);
while (HASH_COUNT(cnt) > 2) {
HashItem *pEntry = hashFindItem(&cnt, fruits[left]);
pEntry->val--;
if (pEntry->val == 0) {
HASH_DEL(cnt, pEntry);
free(pEntry);
}
++left;
}
ans = MAX(ans, right - left + 1);
}
hashFree(&cnt);
return ans;
}
[sol1-Golang]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func totalFruit(fruits []int) (ans int) {
cnt := map[int]int{}
left := 0
for right, x := range fruits {
cnt[x]++
for len(cnt) > 2 {
y := fruits[left]
cnt[y]--
if cnt[y] == 0 {
delete(cnt, y)
}
left++
}
ans = max(ans, right-left+1)
}
return
}

func max(a, b int) int {
if b > a {
return b
}
return a
}

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组 fruits 的长度。

  • 空间复杂度:O(1)。哈希表中最多会有三个键值对,可以看成使用了常数级别的空间。

 Comments
On this page
0904-水果成篮