题目介绍
随机产生数字并传递给一个方法。你能否完成这个方法,在每次产生新值时,寻找当前所有值的中间值(中位数)并保存。
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。 double findMedian() - 返回目前所有元素的中位数。 示例:
addNum(1) addNum(2) findMedian() -> 1.5 addNum(3) findMedian() -> 2 通过次数4,599提交次数8,005
来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/co… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
- 需要维护一个最大堆和一个最小堆
- 每次在最大堆中插入元素,然后调整两个堆的数量,如果最小堆比最大堆打,最小堆弹出一个元素,然后插入到最大堆中,如果最大堆比最小堆多两个元素,最大堆弹出一个元素插入到最小堆中
- 获取中位数,如果是基数直接返回最大堆的头部元素,否则返回最大堆加上最小堆元素之和除2
代码
class Heap {
constructor(heapType) {
// 0是小顶堆,1是大顶堆
this.heapType = heapType || 0
this.data = []
}
// getTep(ind) {}
// 向下调
shiftDown(ind = 0) {
let n = this.size() - 1
while (ind * 2 + 1 <= n) {
let tmp = ind
// 小顶堆是用大于号 this.data[tmp] > this.data[ind * 2 + 1], 大顶堆小于号 this.data[tmp] < this.data[ind * 2 + 1]
if (this.heapType === 0) {
if (this.data[tmp] > this.data[ind * 2 + 1]) tmp = ind * 2 + 1
if (ind * 2 + 2 <= n && this.data[tmp] > this.data[ind * 2 + 2])
tmp = ind * 2 + 2
} else {
if (this.data[tmp] < this.data[ind * 2 + 1]) tmp = ind * 2 + 1
if (ind * 2 + 2 <= n && this.data[tmp] < this.data[ind * 2 + 2])
tmp = ind * 2 + 2
}
if (tmp === ind) break
this.data[tmp] = [this.data[ind], (this.data[ind] = this.data[tmp])][0]
ind = tmp
}
}
// 向上调整
shiftUp(idx) {
let pIdx = null
if (this.heapType === 0) {
while (
((pIdx = Math.floor((idx - 1) / 2)),
// 小顶堆是用大于号 this.data[pIdx] > this.data[idx], 大顶堆用小于号this.data[pIdx] < this.data[idx]
idx && this.data[pIdx] > this.data[idx])
) {
this.data[pIdx] = [this.data[idx], (this.data[idx] = this.data[pIdx])][0]
idx = pIdx
}
} else {
while (
((pIdx = Math.floor((idx - 1) / 2)),
// 小顶堆是用大于号 this.data[pIdx] > this.data[idx], 大顶堆用小于号this.data[pIdx] < this.data[idx]
idx && this.data[pIdx] < this.data[idx])
) {
this.data[pIdx] = [this.data[idx], (this.data[idx] = this.data[pIdx])][0]
idx = pIdx
}
}
}
push(val) {
this.data.push(val)
this.shiftUp(this.size() - 1)
}
pop() {
let res = this.top()
if (this.size() === 0) return
if (this.size() === 1) {
return this.data.pop()
}
this.data[0] = this.data.pop()
this.shiftDown(0)
return res
}
top() {
return this.data[0]
}
size() {
return this.data.length
}
}
var MedianFinder = function() {
this.maxH = new Heap(1)
this.minH = new Heap(0)
this.size = 0
};
/**
* @param {number} num
* @return {void}
*/
MedianFinder.prototype.addNum = function(num) {
this.size++
if (num < this.maxH.top() || !this.size) {
this.maxH.push(num)
} else {
this.minH.push(num)
}
// if (this.minH.size() > this.maxH.size()) {
// this.maxH.push(this.minH.pop())
// }
// if (this.maxH.size() === this.minH.size() + 2) {
// this.minH.push(this.maxH.pop())
// }
while((this.maxH.size() - this.minH.size() !== 1) && this.maxH.size() !== this.minH.size()) {
if (this.minH.size() > this.maxH.size()) {
this.maxH.push(this.minH.pop())
} else {
this.minH.push(this.maxH.pop())
}
}
};
/**
* @return {number}
*/
MedianFinder.prototype.findMedian = function() {
let res = this.maxH.top()
if (this.size % 2 === 0) {
res = (res + this.minH.top()) / 2
}
return res
};
/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/