给定整数数组 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(logN)
- 如果当前的数大于前一个数,保持不变
- 如果当前的数小于等于前一个数,一直加直到大于前一个数
class Solution {
public:
int minIncrementForUnique(vector<int>& A) {
int n = A.size();
if(n == 0) return 0;
sort(A.begin(),A.end());
int res = 0;
for(int i = 1; i < n; i++){
if(A[i] <= A[i-1]){//果当前的数小于等于前一个数,一直加直到大于前一个数
int t = A[i];
A[i] = A[i-1] + 1; //确保肯定大于前一个数
res += A[i] - t;
}
}
return res;
}
};
方法二:
采用计数排序,统计每个元素出现的次数。O(N)
class Solution {
public:
int minIncrementForUnique(vector<int>& A) {
int n = A.size();
if(n == 0) return 0;
vector<int> counter(40000,0); //记录每个数字出现的次数
int maxNum = -1; //保存当前数组中的最大值
for(int i = 0; i < n; i++){
counter[A[i]]++;
maxNum = max(maxNum,A[i]);
}
int res = 0;
// 遍历counter数组,若当前数字的个数cnt大于1个,则只留下1个,其他的cnt-1个后移
for(int num = 0; num <= maxNum; num++){
if(counter[num] > 1){
int t = counter[num] - 1;
res += t;
counter[num+1] += t;
}
}
//counter[maxNum+1]里可能会有从counter[max]后移过来的,counter[maxNum+1]里只留下1个,其它的d个后移。
if(maxNum < 39999){
int t = counter[maxNum+1] - 1;
res += (1 + t)*t/2;
}
return res;
}
};
方法三:
解法
class Solution {
vector<int> pos;
int findPos(int a){
int b = pos[a];
if(b == -1){ //对于的位置时空的,直接放入
pos[a] = a;
return a;
}
//否则向后寻址
// 因为pos[a]中标记了上次寻址得到的空位,因此从pos[a]+1开始寻址就行了(不需要从a+1开始)。
b = findPos(b+1);
pos[a] = b;
return b;
}
public:
int minIncrementForUnique(vector<int>& A) {
int n = A.size();
if(n == 0) return 0;
int res = 0;
pos = vector<int>(80000,-1);//-1代表空位。最坏情况下有40000个重复的数
//遍历每个数字A[i]对其寻地址得到位置b, b比A[i]的增量就是操作数
for(auto a : A){
int b = findPos(a);
res += b - a;
}
return res;
}
};