[LeetCode] 904. Fruit Into Baskets 水果裝入果籃



In a row of trees, the i-th tree produces fruit with type tree[i].html

You start at any tree of your choice, then repeatedly perform the following steps:git

  1. Add one piece of fruit from this tree to your baskets.  If you cannot, stop.
  2. Move to the next tree to the right of the current tree.  If there is no tree to the right, stop.

Note that you do not have any choice after the initial choice of starting tree: you must perform step 1, then step 2, then back to step 1, then step 2, and so on until you stop.github

You have two baskets, and each basket can carry any quantity of fruit, but you want each basket to only carry one type of fruit each.數組

What is the total amount of fruit you can collect with this procedure?ui

Example 1:this

Input: [1,2,1]
Output: 3
Explanation: We can collect [1,2,1].

Example 2:指針

Input: [0,1,2,2]
Output: 3 Explanation: We can collect [1,2,2].
If we started at the first tree, we would only collect [0, 1].

Example 3:code

Input: [1,2,3,2,2]
Output: 4 Explanation: We can collect [2,3,2,2].
If we started at the first tree, we would only collect [1, 2].

Example 4:orm

Input: [3,3,3,1,2,1,1,2,3,3,4]
Output: 5  Explanation: We can collect [1,2,1,1,2].
If we started at the first tree or the eighth tree, we would only collect 4 fruits.

Note:htm

  1. 1 <= tree.length <= 40000
  2. 0 <= tree[i] < tree.length



這道題說是給了咱們一排樹,每棵樹產的水果種類是 tree[i],說是如今有兩種操做,第一種是將當前樹的水果加入果籃中,若不能加則中止;第二種是移動到下一個樹,若沒有下一棵樹,則中止。如今咱們有兩個果籃,能夠從任意一個樹的位置開始,可是必須按順序執行操做一和二,問咱們最多能收集多少個水果。說實話這道題的題目描述確實不太清晰,博主看了不少遍才明白意思,論壇上也有不少吐槽的帖子,但實際上這道題的本質就是從任意位置開始,若最多隻能收集兩種水果,問最多能收集多少個水果。那麼再進一步提取,其實就是最多有兩個不一樣字符的最長子串的長度,跟以前那道 Longest Substring with At Most Two Distinct Characters 如出一轍,只不過換了一個背景,代碼基本均可以直接使用的,博主感受這樣出題有點不太好吧,徹底重複了。以前那題的四種解法這裏徹底均可以使用,先來看第一種,使用一個 HashMap 來記錄每一個水果出現次數,當 HashMap 中當映射數量超過兩個的時候,咱們須要刪掉一個映射,作法是滑動窗口的左邊界 start 的水果映射值減1,若此時減到0了,則刪除這個映射,不然左邊界右移一位。當映射數量回到兩個的時候,用當前窗口的大小來更新結果 res 便可,參見代碼以下:



解法一:

class Solution {
public:
    int totalFruit(vector<int>& tree) {
        int res = 0, start = 0, n = tree.size();
        unordered_map<int, int> fruitCnt;
        for (int i = 0; i < n; ++i) {
            ++fruitCnt[tree[i]];
            while (fruitCnt.size() > 2) {
                if (--fruitCnt[tree[start]] == 0) {
                    fruitCnt.erase(tree[start]);
                }
                ++start;
            }
            res = max(res, i - start + 1);
        }
        return res;
    }
};



咱們除了用 HashMap 來映射字符出現的個數,咱們還能夠映射每一個數字最新的座標,好比題目中的例子 [0,1,2,2],遇到第一個0,映射其座標0,遇到1,映射其座標1,當遇到2時,映射其座標2,每次咱們都判斷當前 HashMap 中的映射數,若是大於2的時候,那麼須要刪掉一個映射,咱們仍是從 start=0 時開始向右找,看每一個字符在 HashMap 中的映射值是否等於當前座標 start,好比0,HashMap 此時映射值爲0,等於 left 的0,那麼咱們把0刪掉,start 自增1,再更新結果,以此類推直至遍歷完整個數組,參見代碼以下:



解法二:

class Solution {
public:
    int totalFruit(vector<int>& tree) {
        int res = 0, start = 0, n = tree.size();
        unordered_map<int, int> fruitPos;
        for (int i = 0; i < n; ++i) {
            fruitPos[tree[i]] = i;
            while (fruitPos.size() > 2) {
                if (fruitPos[tree[start]] == start) {
                    fruitPos.erase(tree[start]);
                }
                ++start;
            }
            res = max(res, i - start + 1);
        }
        return res;
    }
};



後來又在網上看到了一種解法,這種解法是維護一個滑動窗口 sliding window,指針 left 指向起始位置,right 指向 window 的最後一個位置,用於定位 left 的下一個跳轉位置,思路以下:

  • 若當前字符和前一個字符相同,繼續循環。

  • 若不一樣,看當前字符和 right 指的字符是否相同:

    • 若相同,left 不變,右邊跳到 i - 1。

    • 若不一樣,更新結果,left 變爲 right+1,right 變爲 i - 1。

最後須要注意在循環結束後,咱們還要比較結果 res 和 n - left 的大小,返回大的,這是因爲若是數組是 [5,3,5,2,1,1,1],那麼當 left=3 時,i=5,6 的時候,都是繼續循環,當i加到7時,跳出了循環,而此時正確答案應爲 [2,1,1,1] 這4個數字,而咱們的結果 res 只更新到了 [5,3,5] 這3個數字,因此咱們最後要判斷 n - left 和結果 res 的大小。

另外須要說明的是這種解法僅適用於於不一樣字符數爲2個的狀況,若是爲k個的話,仍是須要用上面兩種解法。



解法三:

class Solution {
public:
    int totalFruit(vector<int>& tree) {
        int res = 0, left = 0, right = -1, n = tree.size();
        for (int i = 1; i < n; ++i) {
            if (tree[i] == tree[i - 1]) continue;
            if (right >= 0 && tree[right] != tree[i]) {
                res = max(res, i - left);
                left = right + 1;
            }
            right = i - 1;
        }
        return max(n - left, res);
    }
};



還有一種不使用 HashMap 的解法,這裏咱們使用若干個變量,其中 cur 爲當前最長子數組的長度,a和b爲當前候選的兩個不一樣的水果種類,cntB 爲水果b的連續個數。咱們遍歷全部數字,假如遇到的水果種類是a和b中的任意一個,那麼 cur 能夠自增1,不然 cntB 自增1,由於如果新水果種類的話,默認已經將a種類淘汰了,此時候選水果由類型b和這個新類型水果構成,因此當前長度是 cntB+1。而後再來更新 cntB,假如當前水果種類是b的話,cntB 自增1,不然重置爲1,由於 cntB 統計的就是水果種類b的連續個數。而後再來判斷,若當前種類不是b,則此時a賦值爲b, b賦值爲新種類。最後不要忘了用 cur 來更新結果 res,參見代碼以下:



解法四:

class Solution {
public:
    int totalFruit(vector<int>& tree) {
        int res = 0, cur = 0, cntB = 0, a = 0, b = 0;
        for (int fruit : tree) {
            cur = (fruit == a || fruit == b) ? cur + 1 : cntB + 1;
            cntB = (fruit == b) ? cntB + 1 : 1;
            if (b != fruit) {
                a = b; b = fruit;
            }
            res = max(res, cur);
        }
        return res;
    }
};



Github 同步地址:

https://github.com/grandyang/leetcode/issues/904



參考資料:

https://leetcode.com/problems/fruit-into-baskets/

https://leetcode.com/problems/fruit-into-baskets/discuss/170740/Sliding-Window-for-K-Elements

https://leetcode.com/problems/fruit-into-baskets/discuss/170745/Problem%3A-Longest-Subarray-With-2-Elements

https://leetcode.com/problems/fruit-into-baskets/discuss/170808/Java-Longest-Subarray-with-atmost-2-Distinct-elements



LeetCode All in One 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索