这是我参与8月更文挑战的第 9 天,活动详情查看:8月更文挑战
题目链接
题目描述
给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1。 您可以假设数组的长度最多为10000。
测试用例
输入:[1,2,3]
输出:2
题目分析
前言
各位同学在做这道题的时候,可能会因为没有思路,然后去评论区一看,各个题解一翻,会满脑子问号,“为啥?为啥他们都说要用中位数来解?”,不知道为什么,反倒是用中位数的思路解了出来😶
我们先用一个小的测试样例来进行推算
给出一个已经排好序的测试样例 [0, 1, 2, 6, 8]
通过对数据的观察,可以得知,对首尾的两个数 0,8 最小的移动次数就是在 [0, 8] 之间任意找一个数,他们的固定移动次数都是 8;如果尝试在这个区间外找一个数来计算移动次数,如找 -1,则 0和8 的移动次数则为 10
同理,我们对 1和6 进行最小次数移动的话, [1, 6] 中的任意数,他们固定移动 5次
最后剩下一个中间的数 2,不移动的话,最小次数为 0
对这个参考数的选取则为 [0, 8] ∪ [1, 6] ∪ [2] = 2,他们的最小移动次数就是 8+5+0 = 13
上述思路可以确定,本题的核心点就是寻找中位数,上面分析的是奇数数组,下面分析偶数数组
示例: [0, 1, 2, 6]
1、在 [0, 6] 任意找一个数,固定最小次数 6 2、在 [1, 2] 任意找一个数,固定最小次数 1
中间数的选取条件为 [0, 6] ∪ [1, 2] = [1, 2],即 1或2 都行,最小移动次数为 6+1 = 7
题目答案
上一步分析出来使用中位数是一种合适的解法,那么,代码很容易就出来了
- 对给定的数组进行排序
- 使用双指针,
left指向index=0,right指向index=nums.length-1 - 对左右指针指向的数进行绝对值求差
- 左右指针往中心靠拢
- 处理左右指针在中心处的特殊情况
- 左右指针重叠
- 左右指针相邻
贴上代码
var minMoves2 = function(nums) {
nums.sort((a, b) => a - b);
let index = (nums.length - 1) / 2;
let center = Math.floor((nums[Math.floor(index)] + nums[Math.ceil(index)])) / 2;
return nums.reduce((a, b) => a + Math.abs(b - center), 0);
};