[LeetCode] 945、使數組唯一的最小增量

題目描述

給定整數數組 A,每次 move 操作將會選擇任意 A[i],並將其遞增 1。返回使 A 中的每個值都是唯一的最少操作次數。

輸入:[3,2,1,2,1,7]
輸出:6
解釋:經過 6 次 move 操作,數組將變爲 [3, 4, 1, 2, 5, 7]。
可以看出 5 次或 5 次以下的 move 操作是不能讓數組的每個值唯一的。

解題思路

主要有兩種思路。

  • 排序O(nlogn)O(nlogn)):先排序,再依次遍歷數組元素,若當前元素小於等於它前一個元素,則將其變爲前一個數+1

  • 先計數再遍歷O(n)O(n)):先排序需要O(nlogn)O(nlogn)的時間,比較昂貴,我們嘗試不進行排序的方法。

    例如輸入 [3, 2, 1, 2, 1, 7],計數之後有兩個 1 和兩個 2。我們先看最小的數,兩個 1 重複了,需要有一個增加到 2,這樣 2 的數量變成了三個。在三個 2 中,又有兩個需要增加到 3,然後又出現了兩個 3…… 以此類推,可以計算出需要增加的次數。

    需要注意的是,雖然整數的範圍是 0 到 40000,但是由於整數還會因爲增加而變大,超出 40000 的範圍。例如極端的情況:所有數都是 39999。所以需要對整數中最大的數單獨處理。

參考代碼

先排序

class Solution {
public:
    int minIncrementForUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int cnt = 0;
        for(int i = 1; i < nums.size(); i++){
            if(nums[i] <= nums[i-1]){
                int temp = nums[i];
                nums[i] = nums[i-1] + 1;
                cnt += nums[i] - temp;
            }
        }
        
        return cnt;
    }
};

先計數再遍歷

int maxNum = 40000;  // 題目給定
class Solution {
public:
    int minIncrementForUnique(vector<int>& nums) {
        // counter數組統計每個數字的個數。
        vector<int> counter(40002, 0);  // 0 <= A[i] < 40000
        for(auto num: nums){
            counter[num]++;
        }
        
        // 遍歷counter數組,若當前數字的個數cnt大於1個,則只留下1個,其他的cnt-1個後移
        int cnt = 0;
        for(int num = 0; num <= maxNum; num++){
            if(counter[num] > 1){
                cnt += (counter[num] - 1);
                counter[num+1] += (counter[num] - 1);
            }
        }
        
        // 最後, counter[max+1]裏可能會有從counter[max]後移過來的,counter[max+1]裏只留下1個,其它的d個後移。
        // 設 max+1 = x,那麼後面的d個數就是[x+1,x+2,x+3,...,x+d],
        // 因此操作次數是[1,2,3,...,d],用求和公式求和。
        int d = counter[maxNum+1] - 1;
        cnt += d * (1 + d) / 2;
        return cnt;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章