Skip to content

Commit 034c1d9

Browse files
committed
add medianSlidingWindow
1 parent e2e93be commit 034c1d9

File tree

1 file changed

+56
-1
lines changed

1 file changed

+56
-1
lines changed

copypasta/heap.go

+56-1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ func (h *mh) remove(i int) *viPair { return heap.Remove(h, i).(*viPair) }
203203
// https://codeforces.com/problemset/problem/796/C 1900
204204
// https://codeforces.com/problemset/problem/2009/G2 2200
205205
// https://codeforces.com/problemset/problem/1732/D2 2400 简化版懒删除堆
206+
func newLazyHeap() *lazyHeap {
207+
return &lazyHeap{removeCnt: map[int]int{}}
208+
}
209+
206210
type lazyHeap struct {
207211
sort.IntSlice
208212
removeCnt map[int]int
@@ -241,6 +245,58 @@ func (h *lazyHeap) pushPop(v int) int {
241245
return v
242246
}
243247

248+
// 滑动窗口中位数 / 距离和
249+
// LC480 https://leetcode.cn/problems/sliding-window-median/
250+
// LC3505 https://leetcode.cn/problems/minimum-operations-to-make-elements-within-k-subarrays-equal/
251+
func medianSlidingWindow(nums []int, k int) []float64 {
252+
ans := make([]float64, len(nums)-k+1)
253+
left := newLazyHeap() // 最大堆(元素取反)
254+
right := newLazyHeap() // 最小堆
255+
256+
for i, in := range nums {
257+
// 1. 进入窗口
258+
if left.size == right.size {
259+
left.push(-right.pushPop(in))
260+
} else {
261+
right.push(-left.pushPop(-in))
262+
}
263+
264+
l := i + 1 - k
265+
if l < 0 { // 窗口大小不足 k
266+
continue
267+
}
268+
269+
// 2. 计算答案(中位数)
270+
if k%2 > 0 {
271+
ans[l] = float64(-left.top())
272+
} else {
273+
ans[l] = float64(right.top()-left.top()) / 2
274+
}
275+
276+
// 2. 计算答案(窗口元素到中位数的距离和)
277+
median := -left.top()
278+
s1 := median*left.size + left.sum // sum 取反
279+
s2 := right.sum - median*right.size
280+
_ = s1 + s2 // 距离和
281+
282+
// 3. 离开窗口
283+
out := nums[l]
284+
if out <= -left.top() {
285+
left.remove(-out)
286+
if left.size < right.size {
287+
left.push(-right.pop()) // 平衡两个堆的大小
288+
}
289+
} else {
290+
right.remove(out)
291+
if left.size > right.size+1 {
292+
right.push(-left.pop()) // 平衡两个堆的大小
293+
}
294+
}
295+
}
296+
297+
return ans
298+
}
299+
244300
// 对顶堆:滑动窗口前 k 小元素和
245301
// 保证 1 <= k <= windowSize <= n
246302
// 返回数组 kthSum,其中 kthSum[i] 为 a[i:i+windowSize] 的前 k 小元素和
@@ -329,7 +385,6 @@ func (h *kthHeap) balance(k int) {
329385
// 其它题目
330386
// 求前缀/后缀的最小的 k 个元素和(k 固定)https://www.luogu.com.cn/problem/P4952 https://www.luogu.com.cn/problem/P3963
331387
// - https://www.codechef.com/problems/OKLAMA
332-
// LC480 滑动窗口中位数 https://leetcode.cn/problems/sliding-window-median/
333388
// https://codeforces.com/contest/1374/problem/E2 代码 https://codeforces.com/contest/1374/submission/193671570
334389

335390
// 如果值域比较小,可以用分桶法做到 O(n+U)

0 commit comments

Comments
 (0)