LeetCode 力扣 135. 分發(fā)糖果

題目描述(困難難度)

給 N 個(gè)小朋友分糖,每個(gè)人至少有一顆糖。并且有一個(gè) rating 數(shù)組,如果小朋友的 rating比它旁邊小朋友的 rating 大(不包括等于),那么他必須要比對(duì)應(yīng)小朋友的糖多。問(wèn)至少需要分配多少顆糖。

- 表示糖,舉幾個(gè)例子。

1 0 2
- - -
-   -
總共就需要 5 顆糖。

1 2 2
- - -
  -
總共就需要 4 顆糖。

解法一

根據(jù)題目,首先每個(gè)小朋友會(huì)至少有一個(gè)糖。

如果當(dāng)前小朋友的 rating 比后一個(gè)小朋友的小,那么后一個(gè)小朋友的糖肯定是當(dāng)前小朋友的糖加 1。

比如 ration = [ 5, 6, 7] ,那么三個(gè)小朋友的糖就依次是 1 2 3

如果當(dāng)前小朋友的 rating 比后一個(gè)小朋友的大,那么理論上當(dāng)前小朋友的糖要比后一個(gè)的小朋友的糖多,但此時(shí)后一個(gè)小朋友的糖還沒(méi)有確定,怎么辦呢?

參考 32題 的解法五,利用正著遍歷,再倒著遍歷的思想。

首先我們正著遍歷一次,只考慮當(dāng)前小朋友的 rating 比后一個(gè)小朋友的小的情況。

接著再倒著遍歷依次,繼續(xù)考慮當(dāng)前小朋友的 rating 比后一個(gè)小朋友的小的情況。因?yàn)橹耙呀?jīng)更新過(guò)一次糖果了,此時(shí)后一個(gè)小朋友的糖如果已經(jīng)比當(dāng)前小朋友的糖多了,就不需要進(jìn)行更新了。

舉個(gè)例子

初始化每人一個(gè)糖
1 2 3 2 1 4
- - - - - -.
    
只考慮當(dāng)前小朋友的 rating 比后一個(gè)小朋友的小的情況,后一個(gè)小朋友的糖是當(dāng)前小朋友的糖加 1。
1 < 2
1 2 3 2 1 4
- - - - - -
  -   

2 < 3
1 2 3 2 1 4
- - - - - -
  - -
    -

3 > 2 不考慮

2 > 1 不考慮

1 < 4
1 2 3 2 1 4
- - - - - -
  - -     -
    -
    
倒過(guò)來(lái)重新進(jìn)行
繼續(xù)考慮當(dāng)前小朋友的 rating 比后一個(gè)小朋友的小的情況。此時(shí)后一個(gè)小朋友的糖如果已經(jīng)比當(dāng)前小朋友的糖多了,就不需要進(jìn)行更新。
4 1 2 3 2 1
- - - - - -
-     - -
      -
    
4 > 1 不考慮

1 < 2
4 1 2 3 2 1
- - - - - -
-   - - -
      -    
    
2 < 3,3 的糖果已經(jīng)比 2 的多了,不需要考慮

3 > 2,不考慮

2 > 1,不考慮

所以最終的糖的數(shù)量就是上邊的 - 的和。

代碼的話,我們用一個(gè) candies 數(shù)組保存當(dāng)前的分配情況。

public int candy(int[] ratings) {
    int n = ratings.length;
    int[] candies = new int[n];
    //每人發(fā)一個(gè)糖
    for (int i = 0; i < n; i++) {
        candies[i] = 1;
    }
    //正著進(jìn)行
    for (int i = 0; i < n - 1; i++) {
        //當(dāng)前小朋友的 rating 比后一個(gè)小朋友的小,后一個(gè)小朋友的糖是當(dāng)前小朋友的糖加 1。
        if (ratings[i] < ratings[i + 1]) {
            candies[i + 1] = candies[i] + 1;
        }
    }
    //倒著進(jìn)行
    //下標(biāo)順序就變成了 i i-1 i-2 i-3 ... 0
    //當(dāng)前就是第 i 個(gè),后一個(gè)就是第 i - 1 個(gè)
    for (int i = n - 1; i > 0; i--) {
        //當(dāng)前小朋友的 rating 比后一個(gè)小朋友的小
        if (ratings[i] < ratings[i - 1]) {
            //后一個(gè)小朋友的糖果樹沒(méi)有前一個(gè)的多,就更新后一個(gè)等于前一個(gè)加 1
            if (candies[i - 1] <= candies[i]) {
                candies[i - 1] = candies[i] + 1;
            }

        }
    }
    //計(jì)算糖果總和
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += candies[i];
    }
    return sum;
}

時(shí)間復(fù)雜度:O(n)。

空間復(fù)雜度:O(n)。

解法二

參考 這里。

解法一中,考慮到

如果當(dāng)前小朋友的 rating 比后一個(gè)小朋友的大,那么理論上當(dāng)前小朋友的糖要比后一個(gè)的小朋友的糖多,但此時(shí)后一個(gè)小朋友的糖還沒(méi)有確定,怎么辦呢?

之前采用了倒著遍歷一次的方式進(jìn)行了解決,這里再考慮另外一種解法。

考慮下邊的情況。

對(duì)于第 2 個(gè) rating 4,它比后一個(gè) rating 要大,所以要取決于再后邊的 rating,一直走到 2,也就是山底,此時(shí)對(duì)應(yīng)的糖果數(shù)是 1,然后往后走,走回山頂,糖果數(shù)一次加 1,也就是到 rating 4 時(shí),糖果數(shù)就是 3 了。

再一般化,山頂?shù)奶枪麛?shù)就等于從左邊的山底或右邊的山底依次加 1 。

所以我們的算法只需要記錄山頂,然后再記錄下坡的高度,下坡的高度剛好是一個(gè)等差序列可以直接用公式求和。而山頂?shù)奶枪麛?shù),取決于左邊山底到山頂和右邊山底到山頂?shù)哪膫€(gè)高度大。

而產(chǎn)生山底可以有兩種情況,一種是 rating 產(chǎn)生了增加,如上圖。還有一種就是 rating 不再降低,而是持平。

知道了上邊的想法,基本上就可以寫代碼了,每個(gè)人寫出來(lái)的應(yīng)該都不一樣,在 discuss 區(qū)也看到了很多不同的寫法,下邊說(shuō)一下我的思路。

抽象出四種情況,這里的高度不是 rating 進(jìn)行相減,而是從山底的 rating 到山頂?shù)?rating 經(jīng)過(guò)的次數(shù)。

  1. 左邊山底到山頂?shù)母叨却?,并且右邊山底后繼續(xù)增加。

  2. 左邊山底到山頂?shù)母叨却?,并且右邊山底是平坡?/p>

  3. 右邊山底到山頂?shù)母叨却螅⑶矣疫吷降缀罄^續(xù)增加。

  4. 右邊山底到山頂?shù)母叨却螅⑶矣疫吷降资瞧狡隆?/p>

有了這四種情況就可以寫代碼了。

我們用 total 變量記錄糖果總和, pre 變量記錄前一個(gè)小朋友的糖果數(shù)。如果當(dāng)前的 rating 比前一個(gè)的 rating 大,那么說(shuō)明在走上坡,可以把前一個(gè)小朋友的糖果數(shù)加到 total 中,并且更新 pre 為當(dāng)前小朋友的糖果數(shù)。

如果當(dāng)前的 rating 比前一個(gè)的 rating 小,說(shuō)明開始走下坡,用 down 變量記錄連續(xù)多少次下降,此時(shí)的 pre 記錄的就是從左邊山底到山底的高度。當(dāng)出現(xiàn)平坡或上坡的時(shí)候,將所有的下坡的糖果數(shù)利用等差公式計(jì)算。此外根據(jù) predown 決定山頂?shù)奶枪麛?shù)。

根據(jù)當(dāng)前是上坡還是平坡,來(lái)更新 pre。

大框架就是上邊的想法了,還有一些邊界需要考慮一下,看一下代碼。

public int candy(int[] ratings) {
    int n = ratings.length;
    int total = 0;
    int down = 0;
    int pre = 1;
    for (int i = 1; i < n; i++) {
        //當(dāng)前是在上坡或者平坡
        if (ratings[i] >= ratings[i - 1]) {
            //之前出現(xiàn)過(guò)了下坡
            if (down > 0) {
                //山頂?shù)奶枪麛?shù)大于下降的高度,對(duì)應(yīng)情況 1
                //將下降的糖果數(shù)利用等差公式計(jì)算,單獨(dú)加上山頂
                if (pre > down) {
                    total += count(down);
                    total += pre;
                //山頂?shù)奶枪麛?shù)小于下降的高度,對(duì)應(yīng)情況 3,
                //將山頂也按照等差公式直接計(jì)算進(jìn)去累加
                } else {
                    total += count(down + 1);
                }
                
                //當(dāng)前是上坡,對(duì)應(yīng)情況 1 或者 3
                //更新 pre 等于 2
                if (ratings[i] > ratings[i - 1]) {
                    pre = 2;
                
                //當(dāng)前是平坡,對(duì)應(yīng)情況 2 或者 4
                //更新 pre 等于 1
                } else {
                    pre = 1;
                }
                down = 0;
            //之前沒(méi)有出現(xiàn)過(guò)下坡
            } else {
                //將前一個(gè)小朋友的糖果數(shù)相加
                total += pre;
                //如果是上坡更新當(dāng)前糖果數(shù)是上一個(gè)的加 1
                if (ratings[i] > ratings[i - 1]) {
                    pre = pre + 1;
                //如果是平坡,更新當(dāng)前糖果數(shù)為 1
                } else {
                    pre = 1;
                }

            }
        } else {
            down++;
        }
    }
    //判斷是否有下坡
    if (down > 0) {
        //和之前的邏輯一樣進(jìn)行相加
        if (pre > down) {
            total += count(down);
            total += pre;
        } else {
            total += count(down + 1);
        }
    //將最后一個(gè)小朋友的糖果計(jì)算
    } else {
        total += pre;
    }
    return total;
}

//等差數(shù)列求和
private int count(int n) {
    return (1 + n) * n / 2;
}

這個(gè)算法相對(duì)于解法一的好處就是將空間復(fù)雜度從 O(n) 優(yōu)化到了 O(1)。

解法一雖然空間復(fù)雜度大一些,但是很好理解,正著遍歷,倒著遍歷的思想,每次遇到都印象深刻。解法二主要是對(duì)問(wèn)題進(jìn)行深入考慮,雖然麻煩些,但空間復(fù)雜度確實(shí)優(yōu)化了。

更多詳細(xì)通俗題解詳見(jiàn) leetcode.wang 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一早起來(lái)看到林霍大婚的消息,十年摯友蛻變摯愛(ài),這份愛(ài)情來(lái)的讓人羨慕。十年很短,揮霍間轉(zhuǎn)瞬即逝;十年很長(zhǎng),慢慢走...
    莫景旺閱讀 199評(píng)論 0 1
  • 文/婉悅悠然 碧湖之前世今生——目錄 第二十四章 性命攸關(guān) 第二十五章 安青逃脫 “逆徒!休要胡說(shuō)!”虛谷子的劍又...
    婉悅悠然閱讀 534評(píng)論 34 32
  • 現(xiàn)在社會(huì)踏踏實(shí)實(shí)做正規(guī)項(xiàng)目,一年能賺10萬(wàn),就算是厲害人物了;做擦邊項(xiàng)目,劍走偏鋒,一年輕輕松松賺30萬(wàn),至今已覺(jué)...
    慢性怪人2018閱讀 690評(píng)論 0 0
  • 網(wǎng)紅營(yíng)銷在這10年里取得了十足的進(jìn)步,現(xiàn)在,網(wǎng)紅的力量可以用發(fā)號(hào)施令來(lái)形容了。 那么網(wǎng)紅究竟想從廣告主者獲得什么?...
    于慌閱讀 754評(píng)論 1 0

友情鏈接更多精彩內(nèi)容