|
46 | 46 |
|
47 | 47 | <!-- 这里可写通用的实现逻辑 -->
|
48 | 48 |
|
| 49 | +**方法一:二分查找** |
| 50 | + |
| 51 | +我们注意到,如果一个长度大于等于 $k$ 的子数组的平均值为 $v$,那么最大平均数一定大于等于 $v$,否则最大平均数一定小于 $v$。因此,我们可以使用二分查找的方法找出最大平均数。 |
| 52 | + |
| 53 | +我们考虑二分查找的左右边界分别是什么?左边界 $l$ 一定是数组中的最小值,而右边界 $r$ 则是数组中的最大值。接下来,我们二分查找中点 $mid$,判断是否存在长度大于等于 $k$ 的子数组的平均值大于等于 $mid$。如果存在,那么我们就将左边界 $l$ 更新为 $mid$,否则我们就将右边界 $r$ 更新为 $mid$。当左边界和右边界的差小于一个极小的非负数,即 $r - l < \epsilon$ 时,我们就可以得到最大平均数,其中 $\epsilon$ 表示一个极小的正数,可以取 $10^{-5}$。 |
| 54 | + |
| 55 | +问题的关键在于如何判断一个长度大于等于 $k$ 的子数组的平均值是否大于等于 $v$。 |
| 56 | + |
| 57 | +我们假设在数组 $nums$ 中,存在一个长度为 $j$ 的子数组,元素分别为 $a_1, a_2, \cdots, a_j$,满足其平均值大于等于 $v$,即: |
| 58 | + |
| 59 | +$$ |
| 60 | +\frac{a_1 + a_2 + \cdots + a_j}{j} \geq v |
| 61 | +$$ |
| 62 | + |
| 63 | +那么: |
| 64 | + |
| 65 | +$$ |
| 66 | +a_1 + a_2 + \cdots + a_j \geq v \times j |
| 67 | +$$ |
| 68 | + |
| 69 | +即: |
| 70 | + |
| 71 | +$$ |
| 72 | +(a_1 - v) + (a_2 - v) + \cdots + (a_j - v) \geq 0 |
| 73 | +$$ |
| 74 | + |
| 75 | +可以发现,如果我们将数组 $nums$ 中的每个元素都减去 $v$,那么原问题就转换成了一个求长度大于等于 $k$ 的子数组的元素和是否大于等于 $0$ 的问题。我们可以使用滑动窗口来解决这个问题。 |
| 76 | + |
| 77 | +我们先计算得到数组前 $k$ 个元素与 $v$ 的差值之和 $s$,如果 $s \geq 0$,那么就说明存在长度大于等于 $k$ 的子数组的元素和大于等于 $0$。 |
| 78 | + |
| 79 | +否则,我们继续往后遍历元素 $nums[j]$,假设当前前 $j$ 项元素与 $v$ 的差值之和为 $s_j$,那么我们可以维护在 $[0,..j-k]$ 范围内元素的前缀和与 $v$ 的差值之和的最小值 $mi$,如果存在 $s_j \geq mi$,那么就说明存在长度大于等于 $k$ 的子数组的元素和大于等于 $0$,返回 $true$。 |
| 80 | + |
| 81 | +否则,我们继续往后遍历元素 $nums[j]$,直到遍历完整个数组。 |
| 82 | + |
| 83 | +时间复杂度 $O(n \times \log M)$,其中 $n$ 和 $M$ 分别是数组 $nums$ 的长度以及数组中的最大值和最小值的差值。空间复杂度 $O(1)$。 |
| 84 | + |
49 | 85 | <!-- tabs:start -->
|
50 | 86 |
|
51 | 87 | ### **Python3**
|
52 | 88 |
|
53 | 89 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
54 | 90 |
|
55 | 91 | ```python
|
| 92 | +class Solution: |
| 93 | + def findMaxAverage(self, nums: List[int], k: int) -> float: |
| 94 | + def check(v: float) -> bool: |
| 95 | + s = sum(nums[:k]) - k * v |
| 96 | + if s >= 0: |
| 97 | + return True |
| 98 | + t = mi = 0 |
| 99 | + for i in range(k, len(nums)): |
| 100 | + s += nums[i] - v |
| 101 | + t += nums[i - k] - v |
| 102 | + mi = min(mi, t) |
| 103 | + if s >= mi: |
| 104 | + return True |
| 105 | + return False |
56 | 106 |
|
| 107 | + eps = 1e-5 |
| 108 | + l, r = min(nums), max(nums) |
| 109 | + while r - l >= eps: |
| 110 | + mid = (l + r) / 2 |
| 111 | + if check(mid): |
| 112 | + l = mid |
| 113 | + else: |
| 114 | + r = mid |
| 115 | + return l |
57 | 116 | ```
|
58 | 117 |
|
59 | 118 | ### **Java**
|
60 | 119 |
|
61 | 120 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
62 | 121 |
|
63 | 122 | ```java
|
| 123 | +class Solution { |
| 124 | + public double findMaxAverage(int[] nums, int k) { |
| 125 | + double eps = 1e-5; |
| 126 | + double l = 1e10, r = -1e10; |
| 127 | + for (int x : nums) { |
| 128 | + l = Math.min(l, x); |
| 129 | + r = Math.max(r, x); |
| 130 | + } |
| 131 | + while (r - l >= eps) { |
| 132 | + double mid = (l + r) / 2; |
| 133 | + if (check(nums, k, mid)) { |
| 134 | + l = mid; |
| 135 | + } else { |
| 136 | + r = mid; |
| 137 | + } |
| 138 | + } |
| 139 | + return l; |
| 140 | + } |
| 141 | + |
| 142 | + private boolean check(int[] nums, int k, double v) { |
| 143 | + double s = 0; |
| 144 | + for (int i = 0; i < k; ++i) { |
| 145 | + s += nums[i] - v; |
| 146 | + } |
| 147 | + if (s >= 0) { |
| 148 | + return true; |
| 149 | + } |
| 150 | + double t = 0; |
| 151 | + double mi = 0; |
| 152 | + for (int i = k; i < nums.length; ++i) { |
| 153 | + s += nums[i] - v; |
| 154 | + t += nums[i - k] - v; |
| 155 | + mi = Math.min(mi, t); |
| 156 | + if (s >= mi) { |
| 157 | + return true; |
| 158 | + } |
| 159 | + } |
| 160 | + return false; |
| 161 | + } |
| 162 | +} |
| 163 | +``` |
| 164 | + |
| 165 | +### **C++** |
| 166 | + |
| 167 | +```cpp |
| 168 | +class Solution { |
| 169 | +public: |
| 170 | + double findMaxAverage(vector<int>& nums, int k) { |
| 171 | + double eps = 1e-5; |
| 172 | + double l = *min_element(nums.begin(), nums.end()); |
| 173 | + double r = *max_element(nums.begin(), nums.end()); |
| 174 | + auto check = [&](double v) { |
| 175 | + double s = 0; |
| 176 | + for (int i = 0; i < k; ++i) { |
| 177 | + s += nums[i] - v; |
| 178 | + } |
| 179 | + if (s >= 0) { |
| 180 | + return true; |
| 181 | + } |
| 182 | + double t = 0; |
| 183 | + double mi = 0; |
| 184 | + for (int i = k; i < nums.size(); ++i) { |
| 185 | + s += nums[i] - v; |
| 186 | + t += nums[i - k] - v; |
| 187 | + mi = min(mi, t); |
| 188 | + if (s >= mi) { |
| 189 | + return true; |
| 190 | + } |
| 191 | + } |
| 192 | + return false; |
| 193 | + }; |
| 194 | + while (r - l >= eps) { |
| 195 | + double mid = (l + r) / 2; |
| 196 | + if (check(mid)) { |
| 197 | + l = mid; |
| 198 | + } else { |
| 199 | + r = mid; |
| 200 | + } |
| 201 | + } |
| 202 | + return l; |
| 203 | + } |
| 204 | +}; |
| 205 | +``` |
| 206 | +
|
| 207 | +### **Go** |
| 208 | +
|
| 209 | +```go |
| 210 | +func findMaxAverage(nums []int, k int) float64 { |
| 211 | + eps := 1e-5 |
| 212 | + l, r := 1e9, -1e9 |
| 213 | + for _, x := range nums { |
| 214 | + l = math.Min(l, float64(x)) |
| 215 | + r = math.Max(r, float64(x)) |
| 216 | + } |
| 217 | + check := func(v float64) bool { |
| 218 | + s := 0.0 |
| 219 | + for _, x := range nums[:k] { |
| 220 | + s += float64(x) - v |
| 221 | + } |
| 222 | + if s >= 0 { |
| 223 | + return true |
| 224 | + } |
| 225 | + t := 0.0 |
| 226 | + mi := 0.0 |
| 227 | + for i := k; i < len(nums); i++ { |
| 228 | + s += float64(nums[i]) - v |
| 229 | + t += float64(nums[i-k]) - v |
| 230 | + mi = math.Min(mi, t) |
| 231 | + if s >= mi { |
| 232 | + return true |
| 233 | + } |
| 234 | + } |
| 235 | + return false |
| 236 | + } |
| 237 | + for r-l >= eps { |
| 238 | + mid := (l + r) / 2 |
| 239 | + if check(mid) { |
| 240 | + l = mid |
| 241 | + } else { |
| 242 | + r = mid |
| 243 | + } |
| 244 | + } |
| 245 | + return l |
| 246 | +} |
| 247 | +``` |
| 248 | + |
| 249 | +### **TypeScript** |
64 | 250 |
|
| 251 | +```ts |
| 252 | +function findMaxAverage(nums: number[], k: number): number { |
| 253 | + const eps = 1e-5; |
| 254 | + let l = Math.min(...nums); |
| 255 | + let r = Math.max(...nums); |
| 256 | + const check = (v: number): boolean => { |
| 257 | + let s = nums.slice(0, k).reduce((a, b) => a + b) - v * k; |
| 258 | + if (s >= 0) { |
| 259 | + return true; |
| 260 | + } |
| 261 | + let t = 0; |
| 262 | + let mi = 0; |
| 263 | + for (let i = k; i < nums.length; ++i) { |
| 264 | + s += nums[i] - v; |
| 265 | + t += nums[i - k] - v; |
| 266 | + mi = Math.min(mi, t); |
| 267 | + if (s >= mi) { |
| 268 | + return true; |
| 269 | + } |
| 270 | + } |
| 271 | + return false; |
| 272 | + }; |
| 273 | + while (r - l >= eps) { |
| 274 | + const mid = (l + r) / 2; |
| 275 | + if (check(mid)) { |
| 276 | + l = mid; |
| 277 | + } else { |
| 278 | + r = mid; |
| 279 | + } |
| 280 | + } |
| 281 | + return l; |
| 282 | +} |
65 | 283 | ```
|
66 | 284 |
|
67 | 285 | ### **...**
|
|
0 commit comments