相遇问题(力扣第462题)
试想两个人,在一条无限长的直线上的两个不同位置,两个人怎么做才能走最少的步数相遇,那就是两个面对面同时朝着对方走,就可以以最少的步数相遇。那么,来看一下这个题:
题目:
给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1。 您可以假设数组的长度最多为10000。
例如:
输入: [1,2,3] 输出: 2 说明: 只有两个动作是必要的(记得每一步仅可使其中一个元素加1或减1): [1,2,3] => [2,2,3] => [2,2,2]
分析:
这就是一个典型的相遇问题,可以把数组中的所有元素想象成在一个数轴上的点,然后通过移动使得这些点都相遇在某一点,如何使得移动的次数最少?那我们首先,可以对这些点进行一下排序,假设相遇点是x,那么数组中经过排序的任意两个数a,b(a<b),都有a<=x<=b,这两个点相向移动才会使得移动的距离最小,所以移动的次数是 (b-x)+(x-a),其实就是b-a;
那么我们要求的是所有数的到达共同的相遇点所移动的距离,其实就是求两两较大数和较小数之间的差值。那我们就假定x是共同的相遇点,它将排序后的数组中数一分为2,即较大的数向左移动,较小的数向右移动,那就使用l和r这两个指针,起始时分别指向数组首部和尾部,移动的距离是num[r] - x + x- num[l],即num[r] - num[l],直到两个指针相遇就结束。
要知道x其实就是较大那部分数和较小那部分中间的任何一个整数,它的值具体是多少没有意义,它的意义在于它相当就是这组数的中位数,这组数要想以最小的移动次数变为全部相等的数,那么就都朝着x进行变化即可。那么就可以形象的理解成为,一对一对的两个数进行着相向移动,二者移动距离之和,一定是二者的数值相减的结果。用数学表达式表示就是:
res = (x-a1) + (x-a2) + (x-a3) + ……(an-1-x) + (an-x)
如果n为偶数,那么x正好全部被抵消掉;如果为奇数,那么最中间的那个数就是中位数,和x相等,二者相减直接就为0;
代码:
public int minMoves2(int[] nums) { if (nums == null || nums.length == 0){ return 0; } Arrays.sort(nums); int l = 0; int r = nums.length - 1; int res = 0; while (l <= r){ res += nums[r] - nums[l]; l++; r--; } return res; }