相遇问题(力扣第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;
}
原文地址:https://www.cnblogs.com/yxym2016/p/13054444.html