一、题目描述
Given a non-empty integer array of size n,
find the minimum number of moves(操作) required to make all array elements equal,
where a move(动作) is incrementing n - 1 elements by 1.
Example:
Input:
[1,2,3]
Output:
3
Explanation:
Only three moves are needed (remember each move increments two elements):
[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
二、题解
(1) 暴力枚举(超时)
@thought
:在最大元素之外执行增加,扫描数组,每次增加后,都更新最大值,又因为最大值会改变,所以,我们每次扫描都只记录本次扫描的最大值下标。
/**
* 时间复杂度:O(n^2 k),其中 n 为数组长度,k 为最大值和最小值的差。
* @date: 1/16/2020 7:41 PM
* @Execution info:超时
*/
public int minMoves1(int[] nums) {
int N = nums.length;
int step=0;
int minIndex=0, maxIndex=N-1;
while (true) {
for (int i = 0; i < N; i++) {
if(nums[maxIndex] < nums[i])
maxIndex = i;
if(nums[minIndex] > nums[i])
minIndex = i;
}
if(nums[maxIndex] == nums[minIndex])
break;
for (int i = 0; i < N; i++) {
// 如果本次扫描的元素下标 != 最大值下标,即非最大值,而不是nums[i] != nums[maxIndex]
if(i != maxIndex)
nums[i]++;
}
step++;
}
return step;
}
复杂度分析
- 时间复杂度: O(n^2 k),n^2 * 每一轮找出最大值最小值差 k 的和。
(2) 优化暴力
@thought
:在minMoves1
中,我们每次找到了最小值,好像并没有去很好地使用minIndex。既然,我们要让每个值都变成本次扫描的最大值,其中必然包括最小值,我们何不直接把本次扫描的最大最小值差nums[maxIndex]-nums[maxIndex]
作为本次循环的加数和移动的步数呢?
/**
* @date: 1/16/2020 8:06 PM
* @Execution info:
* ·执行用时 ms 击败了 % 的java用户
* 内存消耗 MB 击败了 % 的java用户
*/
public int minMoves2(int[] nums) {
int N = nums.length;
int step=0;
int minIndex=0, maxIndex=N-1;
while(true) {
for (int i = 0; i < N; i++) {
if(nums[i] > nums[maxIndex])
maxIndex = i;
if(nums[i] < nums[minIndex])
minIndex = i;
}
int diff = nums[maxIndex]-nums[minIndex];
if(nums[minIndex] == nums[maxIndex])
break;
for (int i = 0; i < N; i++) {
if(i != maxIndex)
nums[i]+=diff;
}
step+=diff;
}
return step;
}
复杂度分析
- 时间复杂度:O(n^2),每次迭代中两个元素是相等的。
(3) 数学思想
@thought
:要让一群数字变成相等状态,可以对非最大值(即,n-1个元素)都进行进行+1
操作,也等价于对每轮的最大值进行-1
操作。
问题就转化为,每个非最小值数与最小值的差值
/**
* @date: 1/16/2020 8:19 PM
* @Execution info:
* 执行用时 3 ms 击败了 71.8% 的java用户
* 内存消耗 39MB 击败了 92% 的java用户
*/
public int minMoves3(int[] nums) {
int step=0;
int min=0x7fffffff;
// for(int n : nums)
// min = Math.min(n, min);
for(int n : nums)
if(n < min) min=n; // 避免每次都执行不必要的赋值操作
for(int n : nums)
step += n-min;
return step;
}