[路飞]_面试题 17.20. 连续中值

71 阅读2分钟

题目地址

题目描述

随机产生数字并传递给一个方法。你能否完成这个方法,在每次产生新值时,寻找当前所有值的中间值(中位数)并保存。

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[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

解题思路

  • 连续中值,寻找中位数,可以用堆来做。申明一个left左边的大根堆,用来存放小值,右边right的小根堆,用来存放大值。
  • addNum方法挂载到原型上,先判断num是否小于left中的堆顶元素,如果小于那么就push到left中。如果不小于就push到right中。对左右堆进去排序。left需要维护一个大根堆,所以是按照从小到大排序。right需要维护一个小根堆,所以是按照从大到小排序。
  • 构造reverse方法,要对新加入的数据,左右堆进行数量的控制。必须保证左边的长度和右边的长度必须相等,或者左边长度比右边长度多1.
  • 最后执行findMedian中值方法,需要先判断当前左右堆得长度,长度一样,那么取出左右堆顶元素进行相加除2.不等那么就返回left大根堆得堆顶元素。

代码

var MedianFinder = function(){
    this.left = [-Infinity];
    this.right = [Infinify];
}
// 保证左右堆得值长度一样或者左堆长度比右堆长度大1
MedianFinder.prototype.reverse = function(num){
    if(this.left.length - this.right.length >= 2){
        this.right.push(this.left.pop());
    }else if(this.right.length > this.left.length){
        this.left.push(this.right.pop());
    }
}
// 判断num是否大于左堆得堆顶元素
MedianFinder.prototype.addNum = function(num){
    if(num <= this.left[this.left.length - 1]){
        this.left.push(num);
        this.left.sort((a, b) => a - b);
    }else{
        this.right.push(num);
        this.right.sort((a, b) => b - a);
    }
    this.reverse();
}
// 根据左右堆的长度来取返回值
MedianFinder.prototype.findMedian = function(){
    if(this.left.length === this.right.lenght){
        return (this.left[this.left.lenght - 1] + this.right[this.right.lenght - 1]) / 2
    }
    return this.left[this.left.length - 1];
}