Leetcode 453. 最小移动次数使数组元素相等 (“相对大小”)

Leetcode 453. 最小移动次数使数组元素相等

【题意】
给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动将会使 n - 1 个元素增加 1。

【示例】
输入: [1,2,3]
输出: 3

【解释】
只需要3次移动(注意每次移动会增加两个元素的值):[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]

【解法一】O (n)

对于 1 2 4 6,从小的考虑起,先将 1 增加至和 2 一样,即 2 2 5 7
这个时候前面两个统一了,考虑将前三个统一,即 5 5 5 10
这时候只剩下最后一个,即 10 10 10 10
所以 ans = (2 - 1) + (4 - 1) + (6 - 1) = 1 + 3 + 5 = 9

为什么能这么做呢:其实在 1 2 4 6 变成 2 2 5 7时,虽然后面的数也跟着增加了,
但是它们和第一个数的相对差值并没有变化,换而言之,这道题本身考虑的大小也是相对的,
所以其实最终答案 = 每一个数减去最小数,再相加即可得

那么还有一个问题就是,上面的例子已经是正序排好,假如是 2 6 1 4呢?
那这其实是不影响的。比如说遍历到 2 的时候,2 不变,后面都 + 1,以便最小的 1 能变成 2,
此时是 2 7 2 5。我们可以发现,因为是拿每一个数去加上和min的差值,后面还没遍历到的数原本
就大于等于min,他们和min的相对值差依然不变。故而,数的顺序并不影响遍历的操作

当然,按照这道题的官方题解,这种解法也可以解释为:
每一次操作只有一个数 a 不变,其余 + 1,这就相当于只有数 a 减 1,其余数不变
因为我们只在意最终什么时候所有数持平,所以其实就是相当于探究每个非min的数,需要减去几,
才能和min持平。
所以这种思路的做法也是加上每个数与min的差值,以O(n)的方式解决。

class Solution {
    
    
    public int minMoves(int[] nums) {
    
    
        int ans = 0;
        long min = (long) 1e12;
        for (int i : nums) {
    
    
            min = Math.min(min, i);
        }
        for (int i : nums) {
    
    
            ans += (i - min);
        }
        return ans;
    }
}

【解法二】 O(n log n)
思路:接着用上面的例子 1 2 4 6
我们可以像上一种做法从最小数 1 开始,其实也可以从最大数 6 着手。
从右至左,6 是最大数,无需改变,以它为基准,扫到 4 ,发现4 和 6 不持平,
于是转变成 3 4 6 6,此时后两个统一了,到了倒数第三个,会发现要把现在的 4
和后面两个 6 拉平,需要操作(6 - 4)* 2 次,转变成 7 8 8 8 ,因为那两个 6
需要轮流轮空。那么此时就剩下一个数不持平,需要操作(8 - 7)* 3 次。

总共就是
(6 - 4) * 1 + (6 - 4) * 2 + (8 - 7) * 3
=(6 - 4)* 1 + (4 - 2) * 2 + (2 - 1) * 3
= 9
换而言之,其实就是 每相邻两个数之差 * 在其右边数组元素个数 之和

class Solution {
    
    
    public int minMoves(int[] nums) {
    
    
        int length = nums.length;
        Arrays.sort(nums);
        int add = 0;
        int ans = 0;
        for (int i = length - 2; i >= 0; -- i) {
    
    
            nums[i] += add;
            add += nums[length - 1] - nums[i];
            ans += (length - 1 - i) * (nums[length - 1] - nums[i]);
        }
        return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/CSDNWudanna/article/details/110733741
今日推荐