題目描述
給定整數數組 A,每次 move 操作將會選擇任意 A[i]
,並將其遞增 1
。返回使 A
中的每個值都是唯一的最少操作次數。
輸入:[3,2,1,2,1,7]
輸出:6
解釋:經過 6 次 move 操作,數組將變爲 [3, 4, 1, 2, 5, 7]。
可以看出 5 次或 5 次以下的 move 操作是不能讓數組的每個值唯一的。
解題思路
主要有兩種思路。
-
排序():先排序,再依次遍歷數組元素,若當前元素小於等於它前一個元素,則將其變爲前一個數
+1
。 -
先計數再遍歷():先排序需要的時間,比較昂貴,我們嘗試不進行排序的方法。
例如輸入
[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;
}
};