Algorithms first

数据流的中位数 · 交互式算法学习

支持数据流插入与 O(1) 查询中位数。

#295 ·

数据流的中位数

Find Median from Data Stream

测试用例addNum(1), addNum(2), findMedian(), addNum(3)
栈 / 队列 / 堆3 个关键状态
步骤 11 进入较小半区。lo=[1]
123
推荐

最大堆 + 最小堆

时间 插入 O(log n)空间 O(n)

小的一半放最大堆,大的一半放最小堆,大小差不超过 1。

1class Heap {2  constructor(compare) {3    this.a = [];4    this.compare = compare5  }6  push(x) {7    const a = this.a;8    a.push(x);9    let i = a.length - 1;10    while (i) {11      const p = (i - 1) >> 1;12      if (!this.compare(a[i], a[p])) break;13      [a[i], a[p]] = [a[p], a[i]];14      i = p15    }16  }17  pop() {18    const a = this.a;19    if (!a.length) return undefined;20    const top = a[0],21      last = a.pop();22    if (a.length) {23      a[0] = last;24      for (let i = 0;;) {25        let best = i,26          l = i * 2 + 1,27          r = l + 1;28        if (l < a.length && this.compare(a[l], a[best])) best = l;29        if (r < a.length && this.compare(a[r], a[best])) best = r;30        if (best === i) break;31        [a[i], a[best]] = [a[best], a[i]];32        i = best33      }34    }35    return top36  }37  peek() {38    return this.a[0]39  }40  get size() {41    return this.a.length42  }43}44class MedianFinder {45  constructor() {46    this.lo = new Heap((a, b) => a > b);47    this.hi = new Heap((a, b) => a < b)48  }49  addNum(x) {50    this.lo.push(x);51    this.hi.push(this.lo.pop());52    if (this.hi.size > this.lo.size) this.lo.push(this.hi.pop())53  }54  findMedian() {55    return this.lo.size > this.hi.size ? this.lo.peek() : (this.lo.peek() + this.hi.peek()) / 256  }57}
多语言参考

Java · C++ · Go

来源 · CC BY-SA 4.0 ↗

这是一组同题正确实现,独立于上方当前动画方法;不同语言可能采用另一种正确策略。

1class MedianFinder {2    private PriorityQueue<Integer> minQ = new PriorityQueue<>();3    private PriorityQueue<Integer> maxQ = new PriorityQueue<>(Collections.reverseOrder());4 5    public MedianFinder() {6    }7 8    public void addNum(int num) {9        maxQ.offer(num);10        minQ.offer(maxQ.poll());11        if (minQ.size() - maxQ.size() > 1) {12            maxQ.offer(minQ.poll());13        }14    }15 16    public double findMedian() {17        return minQ.size() == maxQ.size() ? (minQ.peek() + maxQ.peek()) / 2.0 : minQ.peek();18    }19}20 21/**22 * Your MedianFinder object will be instantiated and called as such:23 * MedianFinder obj = new MedianFinder();24 * obj.addNum(num);25 * double param_2 = obj.findMedian();26 */
交互式算法学习

从执行步骤真正理解 LeetCode 经典 150

本站整理 150 道高频算法面试题和 302 种解法。动画方法同步展示 JavaScript 与 Python;每题另提供 Java、C++ 与 Go 同题参考实现。

多形态可视化数组、矩阵、DP 表、递归树、树图、链表、栈队列堆、区间和状态机按解法选择。
动画与代码同步当前状态、步骤说明、变量和代码高亮保持一一对应,可逐步播放和回退。
适合面试复习每种方案都给出思路、时间复杂度、空间复杂度、测试用例和 LeetCode 中文原题入口。
AlgoViz Lab

数据流的中位数 · 解法对比

支持数据流插入与 O(1) 查询中位数。

测试用例
addNum(1), addNum(2), findMedian(), addNum(3)
题目分类
解法对比
2
方案 1

维护有序数组

二分找到插入位置后数组移动元素,中位数直接读取。

时间
插入 O(n)
空间
O(n)
方案 2

最大堆 + 最小堆

小的一半放最大堆,大的一半放最小堆,大小差不超过 1。

时间
插入 O(log n)
空间
O(n)