[LeetCode] Candy (分糖果),時間復雜度O(n),空間復雜度為O(1),且只需遍歷一次的實現


原題:

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?

 

Solution (1)

這題本身可以用貪心法來做,我們用candy[n]表示每個孩子的糖果數,遍歷過程中,如果孩子i+1的rate大於孩子i 的rate,那么當前最好的選擇自然是:給孩子i+1的糖果數=給孩子i的糖果數+1

如果孩子i+1的rate小於等於孩子i 的rate咋整?這個時候就不大好辦了,因為我們不知道當前最好的選擇是給孩子i+1多少糖果。

解決方法是:暫時不處理這種情況。等數組遍歷完了,我們再一次從尾到頭遍歷數組,這回逆過來貪心,就可以處理之前略過的孩子。

最后累加candy[n]即得到最小糖果數。

 

這種解法是需要O(n)的輔助空間給candy[]的。

有沒有更好的辦法?

Solution (2) 此方法以及代碼部分參考了Shangrila 的方法。

請回想一下:我們為什么需要輔助空間?當孩子的rate是一個非遞減曲線的時候,我們是不需要輔助空間的,比如5個孩子的rate分別是1,2,5,7,10。那么糖果數自然是1,2,3,4,5。又如5個孩子的rate分別是1,2,5,5,10,那么糖果數自然是1,2,3,1,2。

因此如果rate是非遞減數列,我們可以精確計算出當前孩子應該給多少糖果,把這個糖果數加入總數即可。

當孩子的rate出現遞減的情況該如何是好?不用輔助空間能處理嗎?

假設5個孩子的rate是 1,5,4,3,2。我們這樣計算:遍歷時,第一個孩子依然糖果為1,第二個孩子糖果為2,第三個孩子糖果給幾個?我們遍歷到后面就會知道第二個孩子給的糖果太少了,應該給4個。有沒有辦法在遍歷到后面時,能計算出一個修正值,使得加上這個修正值,正好依然可以使總糖果數是正確的?

 

其實這個修正值不難計算,因為可以發現遞減數列的長度決定了第二個孩子該給幾個糖果。仔細觀察:遍歷到第四個孩子時我們知道了第二個孩子不該給2,應該給3,因此Total 要 +=1;遍歷到第五個孩子我們知道了第二個孩子不該給3得給4,因此Total 要 += 1。我們設一個變量beforeDenc表示進入遞減序列之前的那個孩子給的糖果值,再設置length用來表達當前遞減序列的長度。這兩個變量就可以決定Total是不是要修正:當遍歷第三個孩子的時候 beforeDenc = 2,以后每遍歷一個孩子,因為length已經超過了beforeDenc,每次Total都要額外+1,來修正第二個孩子的糖果數。

 

對於后面三個孩子,我們可以這樣計算:遍歷到第三個孩子,因為這是遞減數列的第二個數字,我們Total += 1;第四個孩子是遞減數列的第三個數字,Total += 2;第五個孩子是遞減數列的第四個數字,Total += 3。

可以發現最后三個孩子的糖果總數依然是正確的,雖然Total 每次增加的糖果數量正好和當前孩子得到的糖果數是反序關系。

 

這種邊遍歷邊修正的方法可以保證一次遍歷,不需要O(n)空間下計算出Total的正確值。

代碼:

int candy(vector<int> &ratings) {
    int Total = 0;    /// Total candies
    int length = 0;  /// Continuous descending length of rate
    int nPreCanCnt = 1; /// Previous child's candy count
    int beforeDenc = nPreCanCnt;
    if(ratings.begin() != ratings.end())
    {
        Total++; //Counting the first child's candy (1).
        for(vector<int>::iterator i = ratings.begin()+1; i!= ratings.end(); i++)
        {
            if(*i < *(i-1))
            {
                length++;
                if(beforeDenc <= length)
                {
                    Total++;
                }
                Total += length;
                nPreCanCnt = 1;    //This step is important, it ensures that once we leave the decending sequence, candy number start from 1
            }
            else
            {
                int curCanCnt = 0;
                if(*i > *(i-1))
                { 
                    curCanCnt = (nPreCanCnt + 1);
                }
                else
                {
                    curCanCnt = 1;
                }
                Total += curCanCnt;
                nPreCanCnt = curCanCnt;
                length = 0;    //reset length of decending sequence
                beforeDenc = curCanCnt;
            }
        }
    }
    return Total;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM