题目
给定整数数组 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
来源:力扣(LeetCode)https://leetcode-cn.com/problems/minimum-increment-to-make-array-unique
解题
思路1
这是个中档题,拿到后就感觉会折在运行时间上,果不其然,第一种方法就是将数值遍历一遍,将存在的数字存在一个Set中,将重复的元素存在一个List中,然后再对List中的元素遍历,累加1直到找到Set中不存在的数字,并将其保存在Set中。可惜超时了。
private int minIncrementForUnique1(int[] A) {
Set<Integer> set = new HashSet<>();
List<Integer> remain = new ArrayList<>();
for (int i : A) {
if (set.contains(i)) {
remain.add(i);
} else {
set.add(i);
}
}
Collections.sort(remain);
int result = 0;
for (int r : remain) {
int temp = r;
while (set.contains(temp)) {
temp++;
result++;
}
set.add(temp);
}
return result;
}
思路2
既然反复累加会超时,那么就找找规律把,每个数字要增加到各不相同的结果,比如[1,2,2]
这个用例,很显然可以看出要将一个2加到3,那么就需要几次?3-2
就是答案,所以可以总结出moveStep = target - origin
。origin好弄,一次遍历后存起来就好,target怎么找?想起了以前编程珠玑的第一个题目,搞个数组,第一次遍历把值当成index标记上已经存在,并将重复的数字缓存。将剩余数字排一下序,遍历刚才的标记数组,找到其中未被标记过的index与剩余数字作比较,大于数字则说明可以通过move达到,累加该次布场。
注意考虑极端情况,输入为40000个40000时,怎么办?我们的标记数组下标最大应该到80000.
private int minIncrementForUnique2(int[] A) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < A.length; i++) {
if (array[A[i]] == 0) {
array[A[i]] = -1;
A[i] = -1;
} else {
list.add(A[i]);
}
}
Collections.sort(list);
int result = 0;
for (int i = 0; i < array.length; i++) {
if (list.isEmpty()) {
break;
}
if (array[i] == -1) {
continue;
}
if (array[i] == 0) {
//说明有空位
int remain = list.get(0);
if (i > remain) {
result += i - remain;
list.remove(0);
}
}
}
return result;
}