LeetCode 945. 使数组唯一的最小增量
题目描述

题意分析
每次可以把一个元素增加 1,求使所有元素互不相同的最少操作次数。最终不要求保持原下标顺序,也不要求相邻值差恰好为 1;原值已经足够大时可以直接保留。题目中的原值非负,代码会先排序。
解法:排序 + 贪心
核心思路
[!blue]
排序后可以按从小到大的原值依次安排目标。设
x <= y,却给它们分配了反向的目标u > v。因为v >= y >= x,交换两个目标后,x仍只需增加到v,y也能增加到更大的u;总增加量不变。因此任意最优方案都能整理成目标值按原值顺序严格递增的方案。已确定的前一个目标为
prev,当前原值为num。当前既不能小于原值,又必须大于prev,所以最小合法目标恰好是max(num, prev + 1)。若某个可行安排把当前元素放得更高,将它降到这个最小目标,不会碰到前一个目标,也不会碰到后面更大的目标,只会减少操作。依次这样调整,就得到贪心方案,所以每一步取最小合法值能取得全局最少增量。
把
target - num加入答案,再令prev = target。后续只需知道上一目标,不必把所有调整结果写回数组。
解题步骤
- 将数组升序排序,初始化总操作数
moves = 0。- 初始化一个小于所有合法原值的
prev。Go 使用 -1,依赖本题原值非负;Java 使用Integer.MIN_VALUE。- 逐个读取
num,取target = max(num, prev + 1),累计target - num。- 将
prev更新为target,继续处理后续元素,最终返回moves。若原值已经严格递增,每项都无需增加。
代码实现
class Solution {
public int minIncrementForUnique(int[] nums) {
Arrays.sort(nums);
int moves = 0;
int prev = Integer.MIN_VALUE;
for (int num : nums) {
// 取不小于原值且严格超过上一目标的最小数。
int target = Math.max(num, prev + 1);
// 只累计实际增加量,下一轮使用调整后的目标。
moves += target - num;
prev = target;
}
return moves;
}
}
import "sort"
func minIncrementForUnique(nums []int) int {
sort.Ints(nums)
moves := 0
prev := -1
for _, num := range nums {
// 取不小于原值且严格超过上一目标的最小数。
target := num
if target <= prev {
target = prev + 1
}
// 只累计实际增加量,下一轮使用调整后的目标。
moves += target - num
prev = target
}
return moves
}
复杂度分析
- 时间复杂度:$O(n\log n)$。排序后只需一次线性扫描。
- 空间复杂度:扫描使用 $O(1)$ 额外空间,另计排序的辅助空间。
关键点总结
[!green]
- 排序前只要求最终值互异,排序后才能把目标统一安排成严格递增序列。
- 最小合法目标同时满足“不能减小”和“不能与此前目标重复”。
prev保存已经调整后的目标,而非上一项原值。
易错点总结
[!yellow]
- 只把重复值增加一次,可能仍与前面已经抬高的目标冲突,必须比较
prev。- 没有排序就要求目标按原顺序递增,会额外加入题目没有要求的顺序限制。
- 操作数是每项的
target - num,不是相邻原值差或相邻目标差。- 当前实现只把输入排序,后续目标仅用于计算;不能把调用后的数组误认为最终互异结果。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 1827. 最少操作使数组递增 | 简单 | 原题保持原下标顺序并使序列严格递增,本题只要求值互异,可排序后贪心抬高冲突值。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!