leetcode
leetcode 1951 ~ 2000
最小平均差

最小平均差

难度:

标签:

题目描述

代码结果

运行时间: 135 ms, 内存: 25.0 MB


/*
 * 思路:
 * 1. 使用流计算 nums 的总和。
 * 2. 通过流来遍历每个下标,计算前 i + 1 个元素和后 n - i - 1 个元素的平均值,然后计算绝对差。
 * 3. 通过流操作找到最小绝对差的下标。
 */
import java.util.stream.IntStream;
public int minimumAverageDifference(int[] nums) {
    int n = nums.length;
    long totalSum = IntStream.of(nums).asLongStream().sum();
    long[] preSum = new long[n];
    preSum[0] = nums[0];
    for (int i = 1; i < n; i++) {
        preSum[i] = preSum[i - 1] + nums[i];
    }
    return IntStream.range(0, n).reduce((i, j) -> {
        long frontAvg = preSum[i] / (i + 1);
        long backAvg = (n - i - 1 == 0) ? 0 : (totalSum - preSum[i]) / (n - i - 1);
        long diff1 = Math.abs(frontAvg - backAvg);
        long frontAvg2 = preSum[j] / (j + 1);
        long backAvg2 = (n - j - 1 == 0) ? 0 : (totalSum - preSum[j]) / (n - j - 1);
        long diff2 = Math.abs(frontAvg2 - backAvg2);
        return diff1 <= diff2 ? i : j;
    }).orElse(0);
}

解释

方法:

此题解采用前缀和和后缀和的概念来计算每个下标处的平均差。使用两个变量pre和suf分别存储从数组开始到当前索引的元素之和(前缀和),以及当前索引后所有元素的和(后缀和)。通过逐步更新这两个和,我们可以有效地计算任意位置的平均差。循环遍历每个元素,更新前缀和与后缀和,并计算当前下标的平均差。如果计算得到的平均差小于之前的最小平均差,则更新最小平均差和对应的下标。最后,单独处理数组最后一个元素的情况,因为其后没有元素,后缀和为0。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在计算平均差时需要使用整数除法而不是常规的浮点数除法?
在此题解中使用整数除法而不是浮点数除法是为了确保结果的稳定性和减少计算复杂度。整数除法(使用 // 运算符)会将结果直接向下取整到最近的整数,这样可以避免因为浮点数精度问题引起的不稳定性,特别是在比较差值时。此外,整数运算通常比浮点运算更快,有利于提高算法的执行效率。
🦆
在计算平均差的过程中,当数组长度n为1时,算法如何处理,是否会存在除以0的错误?
当数组长度n为1时,算法中的循环会被跳过,因为它的范围是从0到n-2。因此,不会执行任何除以0的操作。在这种情况下,算法直接在循环外部处理最后一个元素,即只有一个元素的情况。这时,前缀和等于数组的唯一元素,后缀和为0,而整个数组的平均值也即是这个唯一元素的值,所以平均差为0。
🦆
题解中提到的‘单独处理最后一个元素的情况’是基于什么考虑?最后一个元素的后缀和不是应该为0吗?
单独处理最后一个元素的情况是因为在遍历过程中,最后一个元素的后缀和确实为0(因为没有更多的元素)。这种特殊情况需要单独处理,因为它只有前缀和而没有有效的后缀和。在前面的循环中,我们比较的是前缀和后缀的平均值,而对于最后一个元素,我们只需考虑前缀和(即整个数组的和)与整个数组长度的平均值。这是为了确保所有可能的平均差值都被考虑到,包括数组末尾的情况。
🦆
当数组中所有元素相等时,这种方法是否仍然有效?结果是否始终为数组的第一个或最后一个索引?
当数组中所有元素相等时,这种方法仍然有效。无论选择哪个元素作为分割点,前缀和和后缀和的平均值都将相同,因为所有元素都是相等的。这意味着计算出的平均差将是0。在这种情况下,最小平均差的位置可以是任何一个索引,但根据题解中的实现,如果所有平均差相同,会返回找到的第一个最小值的索引,即0。

相关问题