462. 最少移动次数使数组元素相等 II、
给你一个长度为 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
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-moves-to-equal-array-elements-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处
题目解析:
每次移动,可以将数组中的某一个数字加 1 或者减 1。求最少移动多少次,能让数组中的数字都相等
分析
假设经过移动以后,所有的数字最终都等于 targettarget。
那么这个 targettarget 的取值有两种情况:在数组取值范围外、在数组取值范围内。
我们分情况讨论。
情况一:targettarget 在数组取值范围外
targettarget 在数组外的含义是 target > 数组最大值target>数组最大值或者 target < 数组最小值target<数组最小值。
假如 targettarget 在数组取值范围之外,那么把所有数字移动到 targettarget 一定比移动到边界的移动次数更多!
以 nums = [1, 2, 3]nums=[1,2,3],target = 4target=4 为例,说明为什么 targettarget 不能在数组之外,见图:
因此,target一定在 nums 的最小值和最大值之间。
情况二:targettarget 在数组取值范围内
当 targettarget在数组取值范围内,那么无论 targettarget 选择何值,对于数组的最小值和最大值而言,它们移动到 targettarget 的次数之和一定是固定的!都等于 最大值 - 最小值最大值−最小值。
下图以 nums = [1, 2, 3, 4, 5, 6]nums=[1,2,3,4,5,6],target = 4target=4 为例,说明当 targettarget 取值在 [1,6][1,6] 范围内,数字 11 与 数字 66 与 targettarget 的距离之和是定值 5;
然后,我们考虑去除 numsnums 的最大值、最小值以后的「子数组」,我们发现 targettarget 也需要选择「子数组」范围之内的数字。
因为「情况一」中已经证明了,选择「子数组」取值范围外的数字不是最优。
如下图所示, nums = [1, 2, 3, 4, 5, 6]nums=[1,2,3,4,5,6] ,当 targettarget 取 11 或者 66 时,对于子数组 [2,3,4,5][2,3,4,5] 不是最优:
综上, targettarget 必须是不断选择 数组(以及子数组)最大值、最小值之间的数字,最终就是「中位数」。
根据上面的分析,可以得出结论:
- 当数组的长度是偶数时,targettarget 可以选择 22 个中位数任何一个,总移动次数相等。
- 当数组的长度是奇数时,中位数只有 11 个,因此 targettarget 选择此中位数。
画图说明:
对于数组 nums = [1, 2, 3, 4, 5, 6]nums=[1,2,3,4,5,6] 而言,targettarget 一定选择 33 或者 44。取两者任何一个,结果是相等的。
当数组长度为奇数时,target 一定选择中位数。
当数组长度为奇数时,target 一定选择中位数。
代码实现:
class Solution {
public:
int minMoves2(vector<int>& nums) {
sort(nums.begin(), nums.end());
const int N = nums.size();
int mid = nums[N / 2];
int res = 0;
for (int n : nums) {
res += abs(n - mid);
}
return res;
}
};