904. Fruit Into Baskets
題目連結: 904. Fruit Into Baskets
題目描述
在一排果樹中,每棵樹上都有一種水果,編號為 fruits[i]
。你有兩個籃子,每個籃子只能放一種水果,但每個籃子可以放無限多個該種水果。
你可以從果樹的任意位置開始採摘,但一旦開始,就必須一直往前,不能回頭。你需要找到一段最長的連續子陣列,使得其中包含的水果種類不超過兩種,並返回該子陣列的長度。
限制條件:
1 <= fruits.length <= 10^5
0 <= fruits[i] < fruits.length
解題思路
這個問題可以轉化為在一個整數陣列中,找到包含最多兩種不同數字的最長連續子陣列的長度。這是一個典型的滑動視窗(Sliding Window)問題。
主要步驟
- 初始化:
- 使用兩個指標
i
和j
,分別代表視窗的左端和右端。 - 使用一個雜湊表(
unordered_map
)m
,用於記錄視窗中每種水果的數量。 - 使用變數
count
,記錄視窗中不同水果的種類數。 - 使用變數
ans
,記錄目前為止找到的最長子陣列長度。
- 使用兩個指標
- 擴展視窗:
- 遍歷陣列
fruits
,右指標j
從0
遍歷到n - 1
。 - 將當前水果
fruits[j]
的數量在雜湊表中加一。 - 如果該水果是新種類(即數量從
0
增加到1
),則將count
加一。
- 遍歷陣列
- 收縮視窗:
- 當
count > 2
(即視窗中有超過兩種水果)時,需要收縮視窗:- 減少左端水果
fruits[i]
的數量。 - 如果某種水果的數量減少到
0
,將count
減一。 - 左指標
i
右移一位。
- 減少左端水果
- 當
- 更新答案:
- 在每次擴展視窗後,更新
ans = max(ans, j - i + 1)
,記錄最大的子陣列長度。
- 在每次擴展視窗後,更新
程式碼
class Solution {
public:
int totalFruit(vector<int>& fruits) {
const int n = fruits.size();
int i = 0;
unordered_map<int, int> m;
int count = 0;
int ans = 0;
for (int j = 0; j < n; j += 1) {
if (m[fruits[j]] == 0) {
count += 1;
}
m[fruits[j]] += 1;
while (count > 2) {
if (m[fruits[i]] == 1) {
count -= 1;
}
m[fruits[i]] -= 1;
i += 1;
}
ans = max(ans, j - i + 1);
}
return ans;
}
};
時間與空間複雜度分析
時間複雜度:O(n)
- 解釋:
- 變數
n
為陣列fruits
的長度。 - 雖然外部有一個
for
迴圈,內部有一個while
迴圈,看似是二重迴圈,但實際上每個元素最多被加入和移出視窗一次。 - 指標
j
和i
最多各自遍歷整個陣列一次,因此總時間複雜度為線性O(n)
。
- 變數
空間複雜度:O(1)
- 解釋:
- 雖然使用了雜湊表
m
,但其中最多只會存儲兩種水果的種類(鍵值對)。 count
變數的最大值為2
。- 因此,使用的額外空間是常數級別的,為
O(1)
。
- 雖然使用了雜湊表
優化方案
優化空間複雜度至 O(1)
- 思路:
- 由於我們只需要追蹤兩種水果,我們可以使用兩個變數來記錄這兩種水果的類型,以及它們最後出現的位置。
- 在遍歷陣列時,當遇到第三種水果時,更新左指標
i
,將視窗縮小到只包含當前的兩種水果。
- 實現:
- 使用兩個變數
fruit1
和fruit2
,以及對應的最後出現位置last1
和last2
。 - 每次更新後,計算當前視窗的長度,更新答案。
- 使用兩個變數
- 優點:
- 不再需要使用雜湊表,空間複雜度降低到常數級別。
- 由於只需追蹤兩種水果,程式邏輯更為簡潔。
Comments ()