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

本文详细解析了LeetCode 453题“最小移动次数使数组元素相等”的四种解法,包括暴力计算、暴力法优化、逆向思维及逆向思维优化,对比了不同解法的时间和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

示例:

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

题解:
方案一:暴力计算
时间复杂度:O(n2k)O(n^2k)O(n2k):其中 nnn 为数组长度,kkk为最大值和最小值的差。
空间复杂度:O(1)O(1)O(1):不需要额外空间。
1.先循环遍历一次,找到最大值和最小值对应的数组下标;
2.如果最大值等于最小值,跳出整体循环;
3.再次循环遍历,对除了数组最大值的其他元素均加一;
4.计数器加1;
5.重复这一过程直到最大元素和最小元素相等。
方案二:暴力法优化
时间复杂度:O(n2)O(n^2)O(n2)。每次迭代中两个元素是相等的。
空间复杂度:O(1)O(1)O(1)。不需要额外空间。
在上一方法中,每一步对每个元素增加 1。我们可以在一定程度上改进这一方法。为了让最小元素等于最大元素,至少需要加 kkk次。在那之后,最大元素可能发生变化。因此,我们一次性增加增量 k=max−mink=max-mink=maxmin,并将移动次数增加 kkk。然后,对整个数组进行扫描,找到新的最大值和最小值,重复这一过程直到最大元素和最小元素相等。
方案三:逆向思维
时间复杂度:O(n)O(n)O(n)。对数组进行了一次遍历。
空间复杂度:O(1)O(1)O(1)。不需要额外空间。
n−1n-1n1个数加1相当于给余下的数减1,显然,我们只需要将所有的数都减到最小的数即可,∑i=0n−1a[i]−n∗min(a)\sum_{i=0}^{n-1}a[i]-n*min(a)i=0n1a[i]nmin(a)
方案四:逆向思维优化
时间复杂度:O(n)O(n)O(n)。一次遍历寻找最小值,一次遍历计算次数。
空间复杂度:O(1)O(1)O(1)。不需要额外空间。
上一个方法可能存在问题。∑i=0n−1a[i]\sum_{i=0}^{n-1}a[i]i=0n1a[i]可能会非常大,造成整数越界。为了避免这一问题,我们可以即时计算 ∑i=0n−1(a[i]−min(a))\sum_{i=0}^{n-1}(a[i]-min(a))i=0n1(a[i]min(a))

java代码:

   public static int minMoves(int[] nums) {
        int min = Integer.MAX_VALUE;
        for (int num : nums) {
            min = Math.min(min, num);
        }

        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            count += (nums[i] - min);
        }
        return count;
    }

scala代码:

/**
    * 暴力法
    *
    * @param nums
    * @return
    */
  def minMoves(nums: Array[Int]): Int = {
    var count = 0
    var min = 0
    var max = nums.length - 1
    breakable(
      while (true) {
        for (i <- 0 until nums.length) {
          if (nums(i) > nums(max)) {
            max = i
          }
          if (nums(i) < nums(min)) {
            min = i
          }
        }
        if (nums(min) == nums(max)) {
          break()
        }
        for (i <- 0 until nums.length) {
          if (i != max) {
            nums(i) = nums(i) + 1
          }

        }
        count = count + 1
      }
    )
    count
  }


  /**
    * 暴力法优化
    *
    * @param nums
    * @return
    */
  def minMoves2(nums: Array[Int]): Int = {
    java.util.Arrays.sort(nums)
    var count = 0
    var min = 0
    var max = nums.length - 1
    breakable(
      while (true) {
        for (i <- 0 until nums.length) {
          if (nums(i) > nums(max)) {
            max = i
          }
          if (nums(i) < nums(min)) {
            min = i
          }
        }
        val diff = nums(max) - nums(min)
        if (diff == 0) {
          break()
        }
        count = count + diff
        for (i <- 0 until nums.length) {
          if (i != max) {
            nums(i) = nums(i) + diff
          }

        }
      }
    )
    count
  }

  /**
    * 逆向思维
    * 给n-1个数加1相当于给剩下的一个数减1
    *
    * @param nums
    * @return
    */
  def minMoves3(nums: Array[Int]): Int = {
    var count = 0
    var min = Int.MaxValue
    for (i <- 0 until nums.length) {
      count = count + nums(i)
      min = math.min(min, nums(i))
    }
    count - min * nums.length
  }

  /**
    *逆向思维优化
    * @param nums
    * @return
    */
  def minMoves4(nums: Array[Int]): Int = {
    var count = 0
    var min = Int.MaxValue
    for (i <- 0 until nums.length) {
      min = math.min(min, nums(i))
    }

    for (i <- 0 until nums.length) {
      count = count + (nums(i) - min)
    }
    count
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值