剑指 Offer 41. 数据流中的中位数
和力扣295. 数据流的中位数 一致
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
- void addNum(int num) - 从数据流中添加一个整数到数据结构中。
- double findMedian() - 返回目前所有元素的中位数。
完美的面试回答,你被录取了:
- 在数据流中,数据会不断涌入结构中,那么也就面临着需要多次动态调整以获得中位数。 因此实现的数据结构需要既需要快速找到中位数,也需要做到快速调整。
- 首先能想到就是二叉搜索树,在平衡状态下,树顶必定是中间数,然后再根据长度的奇偶性决定是否取两个数。
- 此方法效率高,但是手动编写较费时费力。
- 根据只需获得中间数的想法,可以将数据分为左右两边,一边以最大堆的形式实现,可以快速获得左侧最大数, 另一边则以最小堆的形式实现。其中需要注意的一点就是左右侧数据的长度差不能超过1。 这种实现方式的效率与AVL平衡二叉搜索树的效率相近,但编写更快
// 两个堆-大顶~小顶 ;用队列建堆//时间:删除logn,查找O1;空间Ontype MedianFinder struct {heapMax *IntHeap1 // 最大堆 -> 代表排序后数据流的左半部分heapMin *IntHeap2 // 最小堆 -> 代表排序后数据流的右半部分}func Constructor() MedianFinder {return MedianFinder{heapMin: &IntHeap2{}, heapMax: &IntHeap1{}}}func (this *MedianFinder) AddNum(num int) {if (*this.heapMax).Len() == 0 {heap.Push(this.heapMax, num)return}m, n := (*this.heapMax).Len(), (*this.heapMin).Len()if m == n+1 {//1, 因为数据流的长度可能为奇数,所以允许最大堆的数量比最小堆的数量多1for num < (*this.heapMax)[0] {heap.Push(this.heapMax, num)num = heap.Pop(this.heapMax).(int)}//2, 最大堆的数量已经比最小堆多1,所以num应该加入最小堆heap.Push(this.heapMin, num)} else {for num > (*this.heapMin)[0] {heap.Push(this.heapMin, num)num = heap.Pop(this.heapMin).(int)}//3, 加入最大堆heap.Push(this.heapMax, num)}}func (this *MedianFinder) FindMedian() float64 {m, n := (*this.heapMax).Len(), (*this.heapMin).Len()if m == n { // 如果列表长度是偶数,中位数则是中间两个数的平均值。return (float64((*this.heapMax)[0]) + float64((*this.heapMin)[0])) / 2.0}return float64((*this.heapMax)[0])}// 最小堆type IntHeap2 []intfunc (h IntHeap2) Len() int { return len(h) }func (h IntHeap2) Less(i, j int) bool { return h[i] < h[j] } // 就这里不同func (h IntHeap2) Swap(i, j int) { h[i], h[j] = h[j], h[i] }func (h *IntHeap2) Push(x interface{}) {*h = append(*h, x.(int))}func (h *IntHeap2) Pop() interface{} {old := *hn := len(old)x := old[n-1]*h = old[0 : n-1]return x}// 最大堆type IntHeap1 []intfunc (h IntHeap1) Len() int { return len(h) }func (h IntHeap1) Less(i, j int) bool { return h[i] > h[j] }func (h IntHeap1) Swap(i, j int) { h[i], h[j] = h[j], h[i] }func (h *IntHeap1) Push(x interface{}) {*h = append(*h, x.(int))}func (h *IntHeap1) Pop() interface{} {old := *hn := len(old)x := old[n-1]*h = old[0 : n-1]return x}
