大家好,我是 @负雪明烛。点击右上方的「+关注,优质题解不间断!

题目大意

每次移动,可以将数组中的某一个数字加 $1$ 或者减 $1$。求最少移动多少次,能让数组中的数字都相等。

解题方法

分析

假设经过移动以后,所有的数字最终都等于 462. 最少移动次数使数组元素相等 II - 图3

那么这个 462. 最少移动次数使数组元素相等 II - 图4的取值有两种情况:在数组取值范围外、在数组取值范围内

我们分情况讨论。

情况一:462. 最少移动次数使数组元素相等 II - 图5在数组取值范围外

462. 最少移动次数使数组元素相等 II - 图6在数组外的含义是462. 最少移动次数使数组元素相等 II - 图7或者 462. 最少移动次数使数组元素相等 II - 图8

462. 最少移动次数使数组元素相等 II - 图9462. 最少移动次数使数组元素相等 II - 图10为例,说明为什么 462. 最少移动次数使数组元素相等 II - 图11 不能在数组之外。

因此,462. 最少移动次数使数组元素相等 II - 图12一定在 462. 最少移动次数使数组元素相等 II - 图13的最小值和最大值之间。

情况二:462. 最少移动次数使数组元素相等 II - 图14%22%20aria-hidden%3D%22true%22%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-74%22%20x%3D%220%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-61%22%20x%3D%22361%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-72%22%20x%3D%22891%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-67%22%20x%3D%221342%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-65%22%20x%3D%221823%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%20%3Cuse%20xlink%3Ahref%3D%22%23E1-MJMATHI-74%22%20x%3D%222289%22%20y%3D%220%22%3E%3C%2Fuse%3E%0A%3C%2Fg%3E%0A%3C%2Fsvg%3E#card=math&code=target&id=rTVNy) 在数组取值范围内

462. 最少移动次数使数组元素相等 II - 图15在数组取值范围内,那么无论 $target$ 选择何值,对于数组的最小值和最大值而言,它们移动到 462. 最少移动次数使数组元素相等 II - 图16的次数之和一定是固定的!都等于 462. 最少移动次数使数组元素相等 II - 图17

当我们考虑去除 462. 最少移动次数使数组元素相等 II - 图18的最大值和最小值后的「子数组」时,我们发现 462. 最少移动次数使数组元素相等 II - 图19不能选择「子数组」范围之外的数字。因为「情况一」中已经证明了,这样的选择不是最优。

综上, 462. 最少移动次数使数组元素相等 II - 图20必须是不断选择 数组(以及子数组)最大值和最小值之间的数字。

对于数组 462. 最少移动次数使数组元素相等 II - 图21而言,462. 最少移动次数使数组元素相等 II - 图22一定选择 462. 最少移动次数使数组元素相等 II - 图23或者 462. 最少移动次数使数组元素相等 II - 图24。取两者任何一个,结果是相同的。

当数组长度为奇数时,462. 最少移动次数使数组元素相等 II - 图25一定选择中位数。

求中位数

如何求中位数?

最简单的方法是将数组 462. 最少移动次数使数组元素相等 II - 图26排序,然后取中间的位置的数字。时间复杂度是 462. 最少移动次数使数组元素相等 II - 图27

当然这也是个经典的 462. 最少移动次数使数组元素相等 II - 图28问题,可以参考 215. 数组中的第K个最大元素 这题的做法。比如「
基于快速排序的选择方法」,可以把时间复杂度降到 462. 最少移动次数使数组元素相等 II - 图29

囿于时间和篇幅问题,我这里不详细解释了。

代码一:排序

  1. 先排序
  2. 再取中位数
  3. 计算将所有数字移动到中位数需要移动多少次,并累加
  1. class Solution(object):
  2. def minMoves2(self, nums):
  3. nums.sort()
  4. return sum([nums[~i] - nums[i] for i in range(len(nums) / 2)])
  1. class Solution {
  2. public:
  3. int minMoves2(vector<int>& nums) {
  4. sort(nums.begin(), nums.end());
  5. const int N = nums.size();
  6. int mid = nums[N / 2];
  7. int res = 0;
  8. for (int n : nums) {
  9. res += abs(n - mid);
  10. }
  11. return res;
  12. }
  13. };

复杂度

  • 时间复杂度:462. 最少移动次数使数组元素相等 II - 图30
  • 空间复杂度:$O(1)$。

代码二:快速选择

可以参考官方题解的做法。

我这里直接使用 C++ 已经实现的 nth_element()函数。

希望读者在评论区中补充哦!

class Solution {
public:
    int minMoves2(vector<int>& nums) {
        const int N = nums.size();
        int mi = N / 2;
        nth_element(nums.begin(), nums.begin() + mi, nums.end());
        int res = 0;
        for (int n : nums) {
            res += abs(n - nums[mi]);
        }
        return res;
    }
};

复杂度

  • 时间复杂度:462. 最少移动次数使数组元素相等 II - 图31
  • 空间复杂度:462. 最少移动次数使数组元素相等 II - 图32

    总结

  1. 本题的重点是为什么要选择中位数。
  2. 计算 462. 最少移动次数使数组元素相等 II - 图33也是经典问题,也需要掌握哦!

我是 @负雪明烛 ,刷算法题 1000 多道,写了 1000 多篇算法题解,收获阅读量 300 万。
关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。