最小操作次数使数组元素相等 II

标签: 数组 数学 排序

难度: Medium

给你一个长度为 n 的整数数组 nums ,返回使所有数组元素相等需要的最小操作数。

在一次操作中,你可以使数组中的一个元素加 1 或者减 1

示例 1:

输入:nums = [1,2,3]
输出:2
解释:
只需要两次操作(每次操作指南使一个元素加 1 或减 1):
[1,2,3]  =>  [2,2,3]  =>  [2,2,2]

示例 2:

输入:nums = [1,10,2,9]
输出:16

提示:

  • n == nums.length
  • 1 <= nums.length <= 105
  • -109 <= nums[i] <= 109

Submission

运行时间: 28 ms

内存: 17.0 MB

class Solution:
    def minMoves2(self, nums: List[int]) -> int:
        n = len(nums)
        nums.sort()
        a = nums[n//2]
        result = 0
        for i in nums:
            result += abs(i-a)
        return result

Explain

这个题解的思路是先将数组排序,然后找到中位数(如果数组长度为奇数则取中间的数,如果为偶数则取最中间两个数的任意一个)。接下来,计算所有数组元素到中位数的绝对差之和,即为最小操作数。

时间复杂度: O(nlogn)

空间复杂度: O(n)

class Solution:
    def minMoves2(self, nums: List[int]) -> int:
        n = len(nums)
        nums.sort()  # 对数组进行排序
        a = nums[n//2]  # 取中位数
        result = 0
        for i in nums:
            result += abs(i-a)  # 计算每个元素与中位数的绝对差,并累加
        return result  # 返回最小操作数

Explore

选择中位数作为目标值可以保证操作数最小化主要因为中位数的属性是最小化所有数据点到该点的距离之和。具体来说,若要最小化一组数到某个固定点的距离之和,最优的固定点就是中位数。这是因为中位数将数据分为数量大致相等的两部分,使得距离的总和达到最小。在统计学中,这称为最小化L1范数(或曼哈顿距离)。

在实际操作中,如果数组长度为偶数,取最中间两个数的任意一个作为中位数通常不会影响最小操作数的结果。这是因为在计算距离的总和时,这两个中位数之间的任何数都会产生同样的总距离。因此,选择其中任意一个中位数都可以达到最小化操作数的目的。

如果元素值的范围非常大,接近整数的极限值,使用这种方法仍然有效,但确实存在溢出的风险。尤其是在某些编程语言中,整数操作可能会导致溢出。为避免这种情况,可以使用支持更大整数范围的数据类型,或者在进行计算时使用溢出检查和安全的数学操作。

题解中的算法完全适用于存在重复元素的情况。重复元素不会影响最小操作次数的计算。中位数仍然是使得总距离最小的最佳目标值,无论数组中的元素是否重复。重复元素的存在不改变中位数的属性,因此不会影响到操作次数的计算。