1. 程式人生 > >Leetcode 945:使陣列唯一的最小增量(超詳細的解法!!!)

Leetcode 945:使陣列唯一的最小增量(超詳細的解法!!!)

給定整數陣列 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

如有問題,希望大家指出!!!