📌  相关文章
📜  通过 k 次操作使所有元素相等的最小增量(1)

📅  最后修改于: 2023-12-03 14:58:02.555000             🧑  作者: Mango

通过 k 次操作使所有元素相等的最小增量

题目描述

给定一个长度为 n 的非空整数数组,你需要找到通过 k 次操作使所有元素相等的最小增量。每次操作可以将选定的一个元素加 1 或减 1。

解题思路

首先我们需要知道一个规律,即将数组排序后,中位数即为所有元素相等的最佳位置。因为比中位数小的元素增加到中位数,比中位数大的元素减少到中位数,总的增量最小。

接下来我们需要计算数组中所有元素到中位数的距离(可能为负数),记为 dist。我们将 dist 中大于等于 0 的元素(即在中位数右侧)和小于 0 的元素(即在中位数左侧)分开计算它们需要移动的距离和操作次数。

当需要移动的距离小于等于 k 时,我们直接将这些元素移动到中位数位置即可。如果需要移动的距离大于 k,我们可以先移动那些需要操作次数较少的元素,即 dist 中值较小的数,因为这些元素需要移动的距离相对较小。

具体地,我们可以将 dist 中大于等于 0 的元素和小于 0 的元素分别从小到大排序,然后从小的开始依次操作,每次操作同时对应两个元素的值,并将 k 减去操作次数。如果 k 不足以进行所有操作,我们就可以直接停止操作,因为后面的元素一定需要更多的操作次数。

最后,我们仍需要进行最后一次操作,使得所有元素到达中位数位置,此时需要移动的距离是 min(k, cnt),其中 cnt 是未移动的元素个数,也就是 dist 中绝对值大于等于 1 的元素个数。

代码实现
class Solution:
    def minMoves(self, nums: List[int], k: int) -> int:
        if k == 1:
            return 0

        # 将所有 1 的下标存储到一个数组中
        idx = [i for i, x in enumerate(nums) if x == 1]
        n = len(idx)

        # 计算所有 1 到中位数的距离
        dist = [idx[i] - idx[(n - 1) // 2 + i] for i in range(n)]

        # 对距离分别处理
        left, right = [], []
        for d in dist:
            if d < 0:
                left.append(d)
            elif d > 0:
                right.append(d)

        # 对 left 和 right 分别排序
        left.sort()
        right.sort()

        # 将所有距离需要移动的距离都放到 left 中
        for i in range(1, len(left)):
            left[i] += left[i - 1]

        # 将 left 和 right 组成一个新的数组 dist
        dist = left + right

        # count 是未移动的元素个数,也就是距离绝对值大于等于 1 的元素个数
        count = len(dist)

        # res 表示需要操作的次数
        res = 0

        # 移动距离小于 k,直接移到中位数位置
        if count <= k:
            res = sum(abs(x) for x in dist)
        else:
            # 先对 dist 排序,从小到大移动
            dist.sort()
            i = j = 0

            # 在移动完所有距离小于 0 的元素之后,如果 k 还不为 0,我们就同时移动一个距离大于 0 的元素
            while k > 0 and i < len(left) and j < len(right):
                if abs(left[i]) <= abs(right[j]):
                    res += abs(left[i])
                    k -= abs(left[i])
                    i += 1
                else:
                    res += abs(right[j])
                    k -= abs(right[j])
                    j += 1

            # 如果 k 还不为 0,就需要继续移动距离大于 0 的元素
            while k > 0:
                count -= 1
                res += 1
                k -= 1

            # 最后一次操作,将所有元素移到中位数位置
            res += min(k, count)

        return res
复杂度分析

本题的时间复杂度为 O(n log n),其中 n 是数组的长度。最坏情况下,需要对距离分别排序,时间复杂度是 O(n log n),而移动所有元素到中位数位置的操作则至少需要 O(n) 的时间。因此总的时间复杂度是 O(n log n)。

空间复杂度为 O(n),需要使用一个数组 idx 来存储所有 1 的下标,以及两个数组 left 和 right 来分别存储距离小于 0 和大于 0 的元素的距离。