数据流的中位数
题目描述
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。 double findMedian() - 返回目前所有元素的中位数。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/find-median-from-data-stream 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
对于一个有序数组arr,对其较小的一半元素,构建一个最大堆,对其较大的一半元素,构建一个最小堆,因此求中位数时:
- 如果两个堆大小一致,则中位数为(最大堆顶+最小堆顶)/2
- 如果最大堆大,则中位数为最大堆对顶,否则为最小堆堆顶
满足以下要求来维护两个堆:
- 最大堆中保存的是有序数组中较小的一部分,最小堆中保存的是较大的一部分,即最小堆中所有元素需要大于最大堆中所有元素
- 两个堆大小之差需要保证在小于等于1
code
type MedianFinder struct {
MinHeap, MaxHeap []int
}
/** initialize your data structure here. */
func Constructor() MedianFinder {
return MedianFinder{
MinHeap: make([]int, 0),
MaxHeap: make([]int, 0),
}
}
func (this *MedianFinder) AddNum(num int) {
if len(this.MinHeap) == 0 {
this.MinHeap = append(this.MinHeap, num)
return
}
if len(this.MaxHeap) == 0 {
this.MaxHeap = append(this.MaxHeap, num)
return
}
if len(this.MinHeap) == 1 && len(this.MaxHeap) == 1 {
if this.MinHeap[0] < this.MaxHeap[0] {
this.MinHeap[0], this.MaxHeap[0] = this.MaxHeap[0], this.MinHeap[0]
}
}
// 如果添加元素大于最小堆堆顶,则向最小堆堆顶添加。
if num > this.MinHeap[0] {
// 需要维护两个堆,保证两个堆的大小之差小于等于1
// 因此最小堆大小已经大于最大堆时,需要先将最小堆堆顶元素添加到最大堆上
if len(this.MinHeap)-len(this.MaxHeap) == 1 {
// 将最小堆堆顶元素添加到最大堆上
this.MaxHeap = append(this.MaxHeap, this.MinHeap[0])
// 调整堆结构,保证最大堆的性质
floatMaxHeap(this.MaxHeap, len(this.MaxHeap)-1)
// 添加新元素
this.MinHeap[0] = num
// 调整堆结构,保证最小堆的性质
adjustMinHeap(this.MinHeap, 0)
} else {
// 不需要交换堆顶元素时,直接在尾部添加新元素,调整堆结构
this.MinHeap = append(this.MinHeap, num)
floatMinHeap(this.MinHeap, len(this.MinHeap)-1)
}
} else {
if len(this.MaxHeap)-len(this.MinHeap) == 1 {
this.MinHeap = append(this.MinHeap, this.MaxHeap[0])
floatMinHeap(this.MinHeap, len(this.MinHeap)-1)
this.MaxHeap[0] = num
adjustMaxHeap(this.MaxHeap, 0)
} else {
this.MaxHeap = append(this.MaxHeap, num)
floatMaxHeap(this.MaxHeap, len(this.MaxHeap)-1)
}
}
// 在添加完元素后,可能出现最大堆堆顶元素大于最小堆堆顶元素的情况,
// 最大堆---较小一部分元素,最小堆---较大一部分元素,
// 因此交换两个堆的堆顶元素,以保证上一条。
if this.MaxHeap[0] > this.MinHeap[0] {
this.MinHeap[0], this.MaxHeap[0] = this.MaxHeap[0], this.MinHeap[0]
adjustMaxHeap(this.MaxHeap, 0)
adjustMinHeap(this.MinHeap, 0)
}
}
func (this *MedianFinder) FindMedian() float64 {
if len(this.MaxHeap) == len(this.MinHeap) {
return (float64(this.MinHeap[0]) + float64(this.MaxHeap[0])) / 2
} else if len(this.MinHeap) > len(this.MaxHeap) {
return float64(this.MinHeap[0])
}
return float64(this.MaxHeap[0])
}
// 自下往上调整堆
func floatMaxHeap(heap []int, i int) {
tmp := heap[i]
for k := (i - 1) / 2; k >= 0; k = (k - 1) / 2 {
if heap[k] < tmp {
i, heap[i] = k, heap[k]
}
if k-1 < 0 {
break
}
}
heap[i] = tmp
}
// 自下往上调整堆
func floatMinHeap(heap []int, i int) {
tmp := heap[i]
for k := (i - 1) / 2; k >= 0; k = (k - 1) / 2 {
if heap[k] > tmp {
i, heap[i] = k, heap[k]
}
if k-1 < 0 {
break
}
}
heap[i] = tmp
}
// 自上往下调整堆
func adjustMaxHeap(heap []int, i int) {
tmp := heap[i]
for k := i*2 + 1; k < len(heap); k = k*2 + 1 {
if k+1 < len(heap) && heap[k+1] > heap[k] {
k++
}
if heap[k] > tmp {
i, heap[i] = k, heap[k]
}
}
heap[i] = tmp
}
// 自上往下调整堆
func adjustMinHeap(heap []int, i int) {
tmp := heap[i]
for k := i*2 + 1; k < len(heap); k = k*2 + 1 {
if k+1 < len(heap) && heap[k+1] < heap[k] {
k++
}
if heap[k] < tmp {
i, heap[i] = k, heap[k]
}
}
heap[i] = tmp
}