🚩传送门:https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements/
题目
给定一个长度为 n
的 非空 整数数组,每次操作将会使 n - 1
个元素增加 1
。找出让数组所有元素相等的最小操作次数。
示例:
输入:[1,2,3] 输出:3 解释: 只需要3次操作(注意每次操作会增加两个元素的值): [1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
解题思路:暴力法 [超时]
我们扫描整个数组以查找最大值和最小元素。此后,我们将 1
添加到除最大元素之外的所有元素,并增加移动数的计数。同样,我们重复相同的过程,直到最大元素和最小元素彼此相等。
复杂度分析
时间复杂度:,其中 是数组的长度, 为最大值和最小值的差。
空间复杂度:,不需要额外空间。
官方代码
public class Solution {
public int minMoves(int[] nums) {
int min = 0, max = nums.length - 1, count = 0;
while (true) {
for (int i = 0; i < nums.length; i++) {
//1. 查找最大值
if (nums[max] < nums[i]) {
max = i;
}
//2. 查找最小值
if (nums[min] > nums[i]) {
min = i;
}
}
//3. 二者相等说明完成操作
if (nums[max] == nums[min]) {
break;
}
//4. 将非最大值集体加1
for (int i = 0; i < nums.length; i++) {
if (i != max) {
nums[i]++;
}
}
count++;
}
return count;
}
}
解题思路:改进暴力法 [超时]
上一方法中,每一步对每个元素增加 1
。我们可以在一定程度上改进这一方法。为了让最小元素等于最大元素,至少需要加 k=max-min
次。在那之后,最大元素可能发生变化。因此,我们一次性增加增量 k
,并将移动次数增加 k
。然后,对整个数组进行扫描,找到新的最大值和最小值,重复这一过程直到最大元素和最小元素相等。
复杂度分析
时间复杂度:,其中 是数组的长度。
空间复杂度:,不需要额外空间。
官方代码
public class Solution {
public int minMoves(int[] nums) {
int min = 0, max = nums.length - 1, count = 0;
while (true) {
for (int i = 0; i < nums.length; i++) {
//1.找到最大值
if (nums[max] < nums[i]) {
max = i;
}
//2.找到最小值
if (nums[min] > nums[i]) {
min = i;
}
}
//3.计算差值
int diff = nums[max] - nums[min];
if (diff == 0) {
break;
}
//4.标记移动步数
count += diff;
//5.依次加上差值 k
for (int i = 0; i < nums.length; i++) {
if (i != max) {
nums[i] = nums[i] + diff;
}
}
}
return count;
}
}
解题思路:利用排序 [通过]
如果对数组进行排序得到有序数列 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[n-2]+diff)-(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
增加的数字相同。
于是,我们可以简单的将数组排序一次,
复杂度分析
时间复杂度:,其中 是数组的长度,排序需要的时间。
空间复杂度:,不需要额外空间。
官方代码
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;
}
}