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

100 阅读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 通过次数4,599提交次数8,005

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/co… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

  1. 需要维护一个最大堆和一个最小堆
  2. 每次在最大堆中插入元素,然后调整两个堆的数量,如果最小堆比最大堆打,最小堆弹出一个元素,然后插入到最大堆中,如果最大堆比最小堆多两个元素,最大堆弹出一个元素插入到最小堆中
  3. 获取中位数,如果是基数直接返回最大堆的头部元素,否则返回最大堆加上最小堆元素之和除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()
 */