[LeetCode] 453. 最小移动次数使数组元素相等

1 题目描述

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

示例:

输入:
[1,2,3]

输出:
3

解释:
只需要3次移动(注意每次移动会增加两个元素的值):

[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2 解题思路

  • 方法一 利用排序 【通过】
    就是一个迭代的过程,从后往前,排序完之后最大的数是a[n], 对之前的所有元素都加上最大和最小的差值,这时候最大的数变成了最后的一个的前一个数a[n-1],最小值一直是最前面的那个也就是a[0]。 以此类推,把所有的累加的次数加起来就是需要移动的次数。

如果对数组进行排序得到有序数列 a,可以有效地简化问题。类似于方法二,我们用 diff=max-min 更新数列。但不同的是,我们不需要每次都遍历整个数组来得到最大和最小值,而是可以利用数组的有序性在 O(1) 时间内找到更新后的最大值和最小值。此外,我们也不需要真的更新数组的值。

为了便于理解,下面逐步讲解该算法。

首先,假设我们在每一步计算 diff 之后正在更新有序数组的元素。下面展示如何在不遍历数组的情况下找到最大最小值。在第一步中,最后的元素即为最大值,因此 diff=a[n-1]-a[0]。我们对除了最后一个元素以外所有元素增加 diff。

现在,更新后的数组开头元素 a’[0] 变成了 a[0]+diff=a[n-1]。因此,a’[0] 等于上一步中最大的元素 a[n-1]。由于数组排过序,直到 i-2 的元素都满足 a[j]>=a[j-1]。因此,更新之后,a’[n-2] 即为最大元素。而 a[0] 依然是最小元素。

于是,在第二次更新时,diff=a[n-2]-a[0]。更新后 a’’[0] 会成为 a’[n-2],与上一次迭代类似。

然后,由于 a’[0] 和 a’[n-1] 相等,在第二次更新后,a’’[0]=a’’[n-1]=a’[n-2]。于是,最大的元素为 a[n-3]。

于是,我们可以继续这样,在每一步用最大最小值差更新数组。

下面进入第二步。第一步中,我们假设每一步会更新数组 a 中的元素。但事实上,我们不需要这么做。这是因为,即使是在更新元素之后,我们要登记的 diff 差值也不变,因为 max 和 min 增加的数字相同。

于是,我们可以简单的将数组排序一次,
在这里插入图片描述
复杂度分析

时间复杂度:O(nlog(n))。 排序需要 O(nlog(n)) 的时间。

空间复杂度:O(1)。不需要额外空间。

  • 方法二:改进的数学法 【通过】

找到最小值,用当前值减去最小值,获取得到要移动的次数,再求和就可以

该方法基于以下思路:将除了一个元素之外的全部元素+1,等价于将该元素-1,因为我们只对元素的相对大小感兴趣。因此,该问题简化为需要进行的减法次数。

显然,我们只需要将所有的数都减到最小的数即可。

复杂度分析
时间复杂度:O(n)。一次遍历寻找最小值,一次遍历计算次数。

空间复杂度:O(1)。不需要额外空间。

作者:LeetCode
链接:https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements/solution/zui-xiao-yi-dong-ci-shu-shi-shu-zu-yuan-su-xiang-d/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3 解决代码

  • 方法一 利用排序 【通过】
public class Solution {
    public int minMoves(int[] nums) {
        //先排序
        Arrays.sort(nums);
        int count = 0;
        for (int i = nums.length - 1; i > 0; i--) {
            //获的每次都要移动的次数
            count += nums[i] - nums[0];
        }
        return count;
    }
}
  • 方法二:改进的数学法 【通过】
class Solution {
    public int minMoves(int[] nums) {
        int moves = 0;
        int min = Integer.MAX_VALUE;
        //求出最小值
        for (int i = 0; i < nums.length; i++){
            min = Math.min(min, nums[i]);
        }
        //要移动的次数等于当前值减去最小值求和
        for(int i = 0; i < nums.length;i++){
            moves += nums[i] - min; 
        }
        return moves;

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值