题目描述
给定整数数组 A,每次 move 操作将会选择任意 A[i],并将其递增 1。
返回使 A 中的每个值都是唯一的最少操作次数。
示例 1:
输入:[1,2,2]
输出:1
解释:经过一次 move 操作,数组将变为 [1, 2, 3]。
示例 2:
输入:[3,2,1,2,1,7]
输出:6
解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7]。
可以看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。
提示:
0 <= A.length <= 40000
0 <= A[i] < 40000
解法一:哈希法(超时)
class Solution {
public int minIncrementForUnique(int[] A) {
int n=A.length;
int count=0;
Map<Integer,Integer> map=new HashMap<>();
for(int i:A){
map.put(i,map.getOrDefault(i,0)+1);
}
for(int i:A){
while(map.containsKey(i)&&map.get(i)>1){
map.put(i,map.get(i)-1);
i++;
if(map.containsKey(i)){
map.put(i,map.get(i)+1);
}
else{
map.put(i,1);
}
count++;
}
map.put(i,1);
}
return count;
}
}
解法二:数组计数
逐个计算
class Solution {
public int minIncrementForUnique(int[] A) {
int n=A.length;
int count=0;
int[] arr=new int[80002];
for(int i:A){
while(arr[i]==1){
i++;
count++;
}
arr[i]=1;
}
return count;
}
}
按序计算
- 全部存入数组,找出最大最小值,逐个扫描
- 个数超过1个的,留下一个,其他的都移动到下一位。
- 最后计算超过max的数列和
class Solution {
public int minIncrementForUnique(int[] A) {
int count=0;
int[] arr=new int[40001];
int max=-1,min=40001;
for(int i:A){
arr[i]++;
max=Math.max(max,i);
min=Math.min(min,i);
}
for(int i=min;i<=max;i++){
if(arr[i]>1){
int d=arr[i]-1;
count+=d;
arr[i+1]+=d;
}
}
int d=arr[max+1]-1;
count+=d*(d+1)/2;
return count;
}
}
解法三:排序
- 排序后,若当前值小于等于前一索引值,则
- count为两者之差+1
- 当前值为前一值+1;
class Solution {
public int minIncrementForUnique(int[] A) {
int count=0;
Arrays.sort(A);
for(int i=1;i<A.length;i++){
if(A[i]<=A[i-1]){
count+=A[i-1]-A[i]+1;
A[i]=A[i-1]+1;
}
}
return count;
}
}
解法四:线性探测
- 先将探测数组用-1填充。
- 往前扫描,找到空位,依次填充。
- 下一次若探测已经出现过的,会跳过重复路径,直接到达空位前一个位置,再探测一次就能找到空位
案例[3, 2, 1, 2, 1, 7]
class Solution {
public int minIncrementForUnique(int[] A) {
int count=0;
int[] arr=new int[80000];
Arrays.fill(arr,-1);
for(int a:A){
int b=findPos(arr,a);
//终点-起点
count+=b-a;
}
return count;
}
public int findPos(int[] arr,int a){
//查看该位置存储的位置。
int b=arr[a];
//若未存过,则存储该位置
if(b==-1){
arr[a]=a;
return a;
}
//向前探测,并且把最终的终点位置往回传
b=findPos(arr,b+1);
//依次存储终点位置,若不加上则超时
arr[a]=b;
return b;
}
}