Leetcode 945:使数组唯一的最小增量(超详细的解法!!!)

版权声明:本文为博主原创文章,未经博主允许不得转载。有事联系:[email protected] https://blog.csdn.net/qq_17550379/article/details/84985831

给定整数数组 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 操作是不能让数组的每个值唯一的。

提示:

  1. 0 <= A.length <= 40000
  2. 0 <= A[i] < 40000

解题思路

这个问题非常简单,我们可以想像成爬楼梯,每次我们爬上一个新的楼梯我们的下一级台阶就会增加1。所以我们可以先对输入数组A进行排序,然后爬楼梯,如果楼梯的高度低于我们的目标高度,我们就记录我们的目标高度和我们实际高度的差加入到result中即可,否则的话就没有任何值加到结果中去。例如[3,2,1,2,1,7]

此时我们计算我们的下一个目标是1+1=2。我们发现1<2,所以我们要将2-1=1添加到结果中去。

此时我们的目标应该是2+1=3。我们发现2<3所以我们要将3-2=1添加到结果中去。依次类推

class Solution:
    def minIncrementForUnique(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        A.sort()
        res, step = 0, 0
        for a in A:
            res += max(step, a) - a
            step = max(step, a) + 1
            
        return res

其实我在一开始的时候想到的不是上面这个解法,我们首先看到了数组的大小只有40000,并不是很大,所以我们就可以开辟这样大小的空间做一些尝试,我们首先想到的做法是Hash表的建立,可以参看这篇文章Hash表的理论基础与具体实现(详细教程)

举个例子,我们遍历3,2,1,2,1,7放到一个大小为40000的盒子中去,并且记录放入元素的个数。

此时我们要做的就是将盒子中包含>=1个元素的位置找到,然后将多余的元素分配出去,分配到哪呢?当然是最近的空位了,这样就可保障我们挪动的步子最少。

同理我们对于所有元素搜这样做,并且我们记录我们挪动的步子总共是多少即可。但是这样做的时候会存在一个问题

这个时候我们的39998号位置的元素多了,但是我们没有空间去存下我们的多余元素了,这样怎么办?我们首先想到的办法是增大分配的数组空间,但是增大多少呢?由于问题的条件0<=A.length<=40000,所以我们开辟一个80000的数组应该就没问题了。我们将上面的过程整理成代码

class Solution:
    def minIncrementForUnique(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        box = [None]*80000
        for a in A:
            if box[a] != None:
                j = a + 1
                while j < 80000:
                    if box[j] == None:
                        box[j] = j
                        break
                    j += 1
            else:
                result[a] = a
            
        box_sum = 0
        for i in box:
            if i != None:
                box_sum += i
        return box_sum - sum(A)

注意,我在实现的时候使用了一点trick,我们可以不用一开始就将所有元素都放入box中去。恩,果然超时了,呵呵。问题在哪?我们每次都要对此时遍历到的元素的后面所有元素做搜索(查找空位的位置),这其中是存在着大量的重复计算的。我们怎么优化呢?我们可以先不将多余的元素放到空位中去,而是放到它的下一个位置堆积起来

就像推动着黄沙向周围摊平。借助这种思想我们就不需要开辟80000的空间了,我们只需要40000就可以搞定这个问题,但是这个时候又有一个问题

此时问题是我们多出的3要怎么摊开?这就是一个等差数列求和的问题,很简单 n ( n + 1 ) / 2 n*(n+1)/2

class Solution:
    def minIncrementForUnique(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        box, result, max_A = [0]*40000, 0, 0
        for a in A:
            box[a] += 1
            if max_A < a:
                max_A = a
                
        for i in range(max_A):
            if box[i] <= 1:
                continue
            ano = box[i] - 1
            result += ano
            box[i+1] += ano
            box[i] = 1
   
        last_ano = box[max_A] - 1
        result += (1 + last_ano)*last_ano//2
        return result

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

猜你喜欢

转载自blog.csdn.net/qq_17550379/article/details/84985831