📅  最后修改于: 2023-12-03 14:58:02.555000             🧑  作者: Mango
给定一个长度为 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 的元素的距离。