寻找中位数我们通常可以从三个角度去思考,第一种方式利用双指针(前提是已经分别排序好),第二种方式利用排序,第三种方式利用堆或者树。这里我们主要介绍利用堆或者树的解题模板。
P295 MedianFinder(难度中等)
解题思路,定义一个最大堆,定义一个最小堆,保证最大堆中的元素大于最小堆中的元素数量并且不超过一个。可以通过一段代码套用模板实现,先插入最大堆,然后将最大堆中的堆顶元素(最大值)插入到最小堆中;如果最大堆中的元素小于最小堆中的元素数量,将最小堆的堆顶元素(最小值)弹出到最大堆中:
1 | public void addNum(int num) { |
根据上面的方式构造的最大最小堆,我们只需要考虑堆顶的元素即可找出中位数,如果是偶数,那么中位数是两个堆顶元素的平均数,如果是奇数,那么中位数是最大堆的堆顶元素。这道题得到的代码如下:
1 | package Design.P295; |
P480 MedianSlidingWindow(难度困难)
这道题不同的是如果只用最大堆和最小堆来实现的话,在删除节点的时候复杂度过高了,为了避免删除节点造成的时间复杂度提升,我们可以改用TreeMap二叉搜索树来实现,将删除节点的复杂度从n减少到了logn。我们套用构建最大树和最小树的模板,发现是极其相似的:
1 | for (int i = 0; i < k; i++) { |
其实这边采用两个和采用一个TreeMap是一样的,只不过始终需要保持左子树和右子树的节点数量差左边大于右边不超过一个。参考一个更加通俗的本题的解题模板。
1 | package SlidingWindow.P480; |
总结
构造最大最小堆/树可以参考其中一个模板。模块的开头已经列出。