【LeetCode-945】使数组唯一的最小增量

问题

给定整数数组 A,每次 move 操作将会选择任意 A[i],并将其递增 1。

返回使 A 中的每个值都是唯一的最少操作次数。

0 <= A.length <= 40000

0 <= A[i] < 40000

示例

输入:[1,2,2]
输出:1
解释:经过一次 move 操作,数组将变为 [1, 2, 3]。

输入:[3,2,1,2,1,7]
输出:6
解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7]。可以看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。

解答1:排序(O(Nlog(N)))

class Solution {
public:
    int minIncrementForUnique(vector<int>& A) {
        sort(A.begin(), A.end());
        int res = 0;
        for (int i = 1; i < A.size(); i++) {
            if (A[i] <= A[i - 1]) {
                res += A[i - 1] - A[i] + 1;
                A[i] = A[i - 1] + 1;
            }
        }
        return res;
    }
};

重点思路

考虑到最终结果排序后一定是一个严格单调的数列,那么就很容易想到先排序,再构造出一个严格单调的数列。move的次数为上一个数比当前数大的值+1,同时当前值变为比上一个数大1的值。

解答2:计数排序(O(N))

class Solution {
public:
    int minIncrementForUnique(vector<int>& A) {
        int mv[40000]; bzero(mv, sizeof mv);
        int minVal = INT_MAX, maxVal = 0, res = 0;
        for (int i : A) {
            minVal = min(minVal, i);
            maxVal = max(maxVal, i);
            mv[i]++;
        }
        for (int i = minVal; i < maxVal; i++) {
            if (mv[i] > 1) {
                res += mv[i] - 1;
                mv[i + 1] += mv[i] - 1;
            }
        }
        res += mv[maxVal] * (mv[maxVal] - 1) / 2;
        return res;
    }
};

重点思路

mv中记录A中对应数字出现的次数,数字作为mv的脚标。为了避免第二个for循环遍历整个mv数组,设置最大最小值。在第二个for循环中,当当前数字出现的次数大于1时,只留一个数不变,其余的都+1,堆到下一个数那里去,到最后处理maxVal处的数,把它们依次排开就好,首项加末项乘项数除以2。

posted @ 2021-02-07 18:06  tmpUser  阅读(59)  评论(0编辑  收藏  举报