一 题目描述
给你一个长度为 n
的整数数组,每次操作将会使 n - 1
个元素增加 1
。返回让数组所有元素相等的最小操作次数。
示例 1:
输入:nums = [1,2,3] 输出:3 解释: 只需要3次操作(注意每次操作会增加两个元素的值): [1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
示例 2:
输入:nums = [1,1,1] 输出:0
二 方法代码
做题思路
三个思路展现出来的最终代码其实是一样的
思路1(也是最简便的)
按照相对理论来说,n-1个元素加1 <=> 一个元素减1,即每一个元素挨个减到最小值,一共要减几次
思路2
来自官方题解评论区的思路,算是正面推导,链接:大佬的评论区链接
思路3
假设一共要操作 k 次,对于最小的数来说,它一定是每一轮都在+1的,因为是它在追赶最高峰,永远不会让他轮空成为那个剩下的不+1的值。对于其他的数来说,其值 - min的结果便是这个数轮空不+1的次数,
所以公式为 k = (其他数 - min)具体原理请见图:
取得最小值:
有三个办法,其中速度最快的是getMin3()
-
方法一,getMin1()
int getMin1(int[] nums){
//Arrays.stream(nums)将数组转换成一个流,可对数组中的元素进行各种操作
//min()是流中的一个终端操作,对于IntStream,会返回一个包含最小值的OptionalInt对象
//因为min()返回的是 OptionalInt,调用getAsInt()方法返回OptionalInt中的整数值
int min = Arrays.stream(nums).min().getAsInt();
return min;
}
-
方法二,getMin2()
int getMin2(int[] nums){
//先排序,这个会改变nums数组,然后拿取第一个
Arrays.sort(nums);
return nums[0];
}
-
方法三,,getMin3()
int getMin3(int[] nums){
int min = nums[0];
for(int num : nums) {
min = Math.min(num, min);
}
return min;
}
总的代码:
public int minMoves(int[] nums) {
int min = getMin3(nums);
int sum = 0;
//计算每一个数和min差值的和
for(int i : nums){
sum += i - min;
}
return sum;
}
三 运行结果
分析速率的区别的原因:
对于getMin1():
- 复杂度:这个方法的时间复杂度是 O(n),因为它只需要遍历数组一次,找到最小值。
- 操作:该方法不会改变数组的顺序,这方法使用了流式 API,虽然同样只遍历数组一次,但涉及到流的创建和
OptionalInt
的处理。这些操作虽然不是很昂贵,但相比手动遍历会有一点额外的开销。 - 开销:流的初始化和包装操作会引入额外的性能开销
对于getMin2():
Arrays.sort(nums)
使用的是双轴快速排序(Dual-Pivot Quicksort),其时间复杂度为 O(nlogn)。- 操作:排序会对整个数组进行重新排列,然后从排好序的数组中取出第一个元素(最小值)。
- 开销:排序操作不仅耗时,还会改变数组的顺序,且需要更多的内存(可能需要额外的空间用于排序)。如果数组很大,开销会更大。
对于getMin3():
- 复杂度:时间复杂度是 O(n),其中 n 是数组的长度。它只需要遍历数组一次,每次比较两个元素并更新最小值。
- 操作:直接遍历数组,逐个元素与当前最小值进行比较,不需要额外的内存或其他操作。
- 优点:这是最直接和高效的方法,因其简单直接,没有额外的函数调用或不必要的操作。