剑指 Offer 41. 数据流中的中位数

剑指 Offer 41. 数据流中的中位数

hard 堆排序

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。 double findMedian() - 返回目前所有元素的中位数。

1
2
3
4
输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]

示例 2:

1
2
3
4
输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

大顶堆 + 小顶堆

https://markdown-1303167219.cos.ap-shanghai.myqcloud.com/image-20220226172633385.png

Picture1.png

https://markdown-1303167219.cos.ap-shanghai.myqcloud.com/image-20220226173122844.png

https://markdown-1303167219.cos.ap-shanghai.myqcloud.com/image-20220226173202297.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import (
	"container/heap"
	"sort"
)

type MedianFinder struct {
	minH iMinHeap // 小顶堆,保存较大的一半
	maxH iMaxHeap // 大顶堆,保存较小的一半
}

func Constructor() MedianFinder {
	maxH, minH := iMaxHeap{}, iMinHeap{}
	heap.Init(&maxH)
	heap.Init(&minH)
	finder := MedianFinder{
		minH: minH,
		maxH: maxH,
	}
	return finder
}

func (mf *MedianFinder) AddNum(num int) {
	if mf.minH.Len() != mf.maxH.Len() {
		heap.Push(&mf.minH, num)
		heap.Push(&mf.maxH, heap.Pop(&mf.minH))
	} else {
		heap.Push(&mf.maxH, num)
		heap.Push(&mf.minH, heap.Pop(&mf.maxH))
	}
}

func (mf *MedianFinder) FindMedian() float64 {
	if mf.minH.Len() == mf.maxH.Len() {
		return float64(mf.minH.Peek()+mf.maxH.Peek()) * 0.5
	}
	return float64(mf.minH.Peek())
}

// ---------------- 大顶堆/小顶堆 定义 ---------------- //
// int类型小顶堆,sort.IntSlice 默认升序,即小顶堆
type iMinHeap struct{ sort.IntSlice }

func (h *iMinHeap) Push(x interface{}) {
	h.IntSlice = append(h.IntSlice, x.(int))
}

func (h *iMinHeap) Pop() interface{} {
	n := h.IntSlice.Len()
	x := h.IntSlice[n-1]
	h.IntSlice = h.IntSlice[:n-1]
	return x
}

// Peek 查看堆顶元素,不改变结构
func (h iMinHeap) Peek() int {
	return h.IntSlice[0]
}

// int类型大顶堆,大顶堆需要重新 Less() 比较器
type iMaxHeap struct{ sort.IntSlice }

func (h iMaxHeap) Less(i, j int) bool {
	return h.IntSlice[i] > h.IntSlice[j]
}

func (h *iMaxHeap) Push(x interface{}) {
	h.IntSlice = append(h.IntSlice, x.(int))
}

func (h *iMaxHeap) Pop() interface{} {
	n := h.IntSlice.Len()
	x := h.IntSlice[n-1]
	h.IntSlice = h.IntSlice[:n-1]
	return x
}

func (h iMaxHeap) Peek() int {
	return h.IntSlice[0]
}