子数组最大平均数 II
难度:
标签:
题目描述
代码结果
运行时间: 127 ms, 内存: 17.1 MB
/*
* Leetcode题目: 子数组最大平均数 II
* 题目描述: 给定一个包含 n 个整数的数组,找出平均值最大且长度至少为 k 的连续子数组,并输出该最大平均值。
* 题目思路:
* 1. 使用二分查找法来解决该问题。
* 2. 首先确定最大平均值的上下界:最大值设为数组的最大值,最小值设为数组的最小值。
* 3. 使用二分法不断缩小平均值的范围,并在每一步验证当前平均值是否可行。
* 4. 验证方法为:遍历数组计算前缀和,并判断是否存在长度至少为 k 的子数组,其平均值大于当前的候选平均值。
*/
import java.util.stream.*;
public class MaxAverageSubarrayStream {
public double findMaxAverage(int[] nums, int k) {
double left = IntStream.of(nums).min().orElse(Integer.MIN_VALUE);
double right = IntStream.of(nums).max().orElse(Integer.MAX_VALUE);
while (right - left > 1e-5) {
double mid = left + (right - left) / 2;
if (canFindLargerAverage(nums, k, mid)) {
left = mid;
} else {
right = mid;
}
}
return left;
}
private boolean canFindLargerAverage(int[] nums, int k, double mid) {
double sum = 0, prev = 0, minSum = 0;
for (int i = 0; i < k; i++) {
sum += nums[i] - mid;
}
if (sum >= 0) return true;
for (int i = k; i < nums.length; i++) {
sum += nums[i] - mid;
prev += nums[i - k] - mid;
minSum = Math.min(minSum, prev);
if (sum >= minSum) return true;
}
return false;
}
}
解释
方法:
此题解采用滑动窗口加上双端队列(deque)的方式来求解问题。首先,通过滑动窗口计算初始大小为k的子数组的平均值。随后,继续滑动窗口并在每一步尝试调整子数组的开始和结束位置,以便找到可能的最大平均值。这一过程中使用了双端队列来维护当前窗口的子数组信息。每次移动时,从前面移除元素并可能从队尾合并较小的平均值子数组,从而不断更新当前的最大平均值。通过队列操作,我们能够有效地计算每一个新窗口的平均值,并比较它与已知的最大平均值。
时间复杂度:
O(N)
空间复杂度:
O(N)
代码细节讲解
🦆
如何确定滑动窗口的起始大小k,并且这个大小对算法的效果有什么影响?
▷🦆
双端队列中元素合并的条件`stack[-1][1] * c1 >= s1 * stack[-1][0]`是如何确保队尾的平均值最大的?能否提供一个具体的例子说明这种合并的情况?
▷🦆
在调整窗口大小的循环中`while stack and stack[0][1] * ct <= st * stack[0][0]`,这个条件是如何帮助找到更大平均值的?具体是通过什么逻辑实现的?
▷🦆
您提到每个元素最多被操作两次,这是否意味着在某些特定情况下,元素的操作次数可以少于两次?如果是,这种情况通常在何时发生?
▷