不管全世界所有人怎么说,我都认为自己的感受才是正确的。无论别人怎么看,我绝不打乱自己的节奏。喜欢的事自然可以坚持,不喜欢的怎么也长久不了。
LeetCode:原题地址
题目要求
随机产生数字并传递给一个方法。你能否完成这个方法,在每次产生新值时,寻找当前所有值的中间值(中位数)并保存。
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。 double findMedian() - 返回目前所有元素的中位数。
示例 1:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
思路
题意很简单,返回中值,可以很容易联想到使用两个堆,大顶堆存放排序后前一半的数据,小顶堆存放排序后后一半的数据;且满足大顶堆的个数和小顶堆的个数关系是maxSize-minSize===0||maxSize-minSize===1,具体思路如下: 1.编写两个类大顶堆类MaxHeap,小顶堆类MinHeap; 2.addNum时,初始直接调用大顶堆的入堆操作,后续的判断大顶堆的堆顶元素和num的关系,如果num小于大顶堆堆顶元素,则入大顶堆,否则入小顶堆,入堆后,会破坏大小顶堆的个数关系,则进行调整,调整的逻辑是哪个堆个数偏大,则删除这个堆的堆顶,并将删除的数入另一个堆,调整一次即可; 3.findMedian,如果两个堆的个数一致,则分别取两个堆的堆顶并除2即可,否则取大顶堆的堆顶元素即可。
代码
/**
* initialize your data structure here.
*/
var MedianFinder = function () {
this.minHeap = new MinHeap();
this.maxHeap = new MaxHeap();
};
/**
* @param {number} num
* @return {void}
*/
MedianFinder.prototype.addNum = function (num) {
if (this.maxHeap.isEmpty()) {
this.maxHeap.push(num)
} else {
if (this.maxHeap.top() > num) {
this.maxHeap.push(num)
} else {
this.minHeap.push(num)
}
let maxSize = this.maxHeap.getSize();
let minSize = this.minHeap.getSize();
if (minSize > maxSize) {
let top = this.minHeap.pop();
this.maxHeap.push(top)
} else if (maxSize - 1 > minSize) {
let top = this.maxHeap.pop();
this.minHeap.push(top)
}
}
};
/**
* @return {number}
*/
MedianFinder.prototype.findMedian = function () {
let maxSize = this.maxHeap.getSize();
let minSize = this.minHeap.getSize();
if (maxSize === minSize) {
return (this.maxHeap.top() + this.minHeap.top()) / 2;
} else {
return this.maxHeap.top();
}
};
class MaxHeap {
constructor() {
this.data = [0];
this.count = 0;
}
isEmpty() {
return this.count === 0;
}
push(val) {
this.count++;
this.data[this.count] = val;
let i = this.count;
while (parseInt(i / 2) > 0 && this.data[i] > this.data[parseInt(i / 2)]) {
this.swap(i, parseInt(i / 2));
i = parseInt(i / 2);
}
}
pop() {
let top = this.data[1]
if (this.count > 1) {
this.data[1] = this.data[this.count];
this.data.splice(this.count, 1);
this.count--;
let i = 1;
while (true) {
let maxPos = i;
if (i * 2 <= this.count && this.data[i] < this.data[i * 2]) {
maxPos = i * 2;
}
if (i * 2 + 1 <= this.count && this.data[maxPos] < this.data[i * 2 + 1]) {
maxPos = i * 2 + 1;
}
if (maxPos === i) {
break;
}
this.swap(i, maxPos);
i = maxPos;
}
} else {
this.count = 0;
this.data.splice(1, 1);
}
return top;
}
swap(i, j) {
let temp = this.data[i];
this.data[i] = this.data[j];
this.data[j] = temp;
}
getSize() {
return this.count;
}
top() {
return this.data[1]
}
}
class MinHeap {
constructor() {
this.data = [0];
this.count = 0;
}
push(val) {
this.count++;
this.data[this.count] = val;
let i = this.count;
while (parseInt(i / 2) > 0 && this.data[i] < this.data[parseInt(i / 2)]) {
this.swap(i, parseInt(i / 2));
i = parseInt(i / 2);
}
}
pop() {
let top = this.data[1]
if (this.count > 1) {
this.data[1] = this.data[this.count];
this.data.splice(this.count, 1);
this.count--;
let i = 1;
while (true) {
let maxPos = i;
if (i * 2 <= this.count && this.data[i] > this.data[i * 2]) {
maxPos = i * 2;
}
if (i * 2 + 1 <= this.count && this.data[maxPos] > this.data[i * 2 + 1]) {
maxPos = i * 2 + 1;
}
if (maxPos === i) {
break;
}
this.swap(i, maxPos);
i = maxPos;
}
} else {
this.count = 0;
this.data.splice(1, 1);
}
return top;
}
swap(i, j) {
let temp = this.data[i];
this.data[i] = this.data[j];
this.data[j] = temp;
}
getSize() {
return this.count;
}
top() {
return this.data[1]
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* var obj = new MedianFinder()
* obj.addNum(num)
* var param_2 = obj.findMedian()
*/