题目描述
思路解析动画文字版
记住这套「新占用值 = max(上一个+1, x),代价 = 新值 - x」,下面每一帧都在套它。
先看原始数组 [3,2,1,2,1,7],里面有两个 1、两个 2,存在重复,需要把它们抬开。
排序后变成 [1,1,2,2,3,7]。相同的数挨在一起,从左往右处理就只需盯住「前一个占用值」。y 先设成 -1,这样第 0 个数的下一个可用值是 0,不会误抬。
轮到第 0 个数 1(紫色)。左边绿色的都是已经定下来、互不重复的值。前一个占用到 -1,所以现在能用的最小值是 0。
1 已经不小于 0,自己就够大,直接占用 1,这一步代价是 0,一步都不用花。
1 原地锁定为绿色,代价 0,总代价还是 0。下一个数要比 1 更大。
轮到第 1 个数 1(紫色)。左边绿色的都是已经定下来、互不重复的值。前一个占用到 1,所以现在能用的最小值是 2。
2 比 1 大,说明 1 占不下,会和前面重复(标红)。只能把它抬到刚好不撞的 2,多走 1 步。
把它从 1 抬到 2,绿色锁定。总代价累加到 1,下一个数要比 2 更大才行。
轮到第 2 个数 2(紫色)。左边绿色的都是已经定下来、互不重复的值。前一个占用到 2,所以现在能用的最小值是 3。
3 比 2 大,说明 2 占不下,会和前面重复(标红)。只能把它抬到刚好不撞的 3,多走 1 步。
把它从 2 抬到 3,绿色锁定。总代价累加到 2,下一个数要比 3 更大才行。
轮到第 3 个数 2(紫色)。左边绿色的都是已经定下来、互不重复的值。前一个占用到 3,所以现在能用的最小值是 4。
4 比 2 大,说明 2 占不下,会和前面重复(标红)。只能把它抬到刚好不撞的 4,多走 2 步。
把它从 2 抬到 4,绿色锁定。总代价累加到 4,下一个数要比 4 更大才行。
轮到第 4 个数 3(紫色)。左边绿色的都是已经定下来、互不重复的值。前一个占用到 4,所以现在能用的最小值是 5。
5 比 3 大,说明 3 占不下,会和前面重复(标红)。只能把它抬到刚好不撞的 5,多走 2 步。
把它从 3 抬到 5,绿色锁定。总代价累加到 6,下一个数要比 5 更大才行。
轮到第 5 个数 7(紫色)。左边绿色的都是已经定下来、互不重复的值。前一个占用到 5,所以现在能用的最小值是 6。
7 已经不小于 6,自己就够大,直接占用 7,这一步代价是 0,一步都不用花。
7 原地锁定为绿色,代价 0,总代价还是 6。下一个数要比 7 更大。
全部处理完,数组变成 [1,2,3,4,5,7],每个值都不一样。把每一步的代价加起来正好是 6,这就是答案。
边界先想清:已唯一就是 0;全相同时逐个抬,代价是 0,1,2 累加。
两个高频追问:计数法替代排序,以及贪心的正确性证明。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def minIncrementForUnique(self, nums: List[int]) -> int: nums.sort() ans, y = 0, -1 for x in nums: y = max(y + 1, x) ans += y - x return ans复杂度
- 时间:O(n log n),排序占主导,之后一遍线性扫描
- 空间:O(1),只用 y、ans 两个变量;排序自身栈开销另算
易错点
面试追问把动画讲成自己的话
追问不排序能不能做?
追问怎么证明贪心是对的?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
行相等的最少多米诺旋转
LeetCode 1007 · 中等 · 沿着 贪心套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题