堆结构-高效维护数据集中最大最小值问题

堆结构介绍

堆结构是一颗完全二叉树,堆结构可以实现O(log n)级别的插入数据的时间复杂度,查询最大最小值可以达到O(1)的效率。

堆结构实现

堆结构维护代码

void put(int data){
    int ch,fa;//child,father
    heap[++heap_size]=data;//heap_size为全局变量 堆内元素个数
    ch = heap_size;//从堆尾开始查找
    while(ch>1){//child不为根
        fa = ch / 2;//父亲位置
        if(heap[ch] <= heap[fa])break;//如果child不够大,就没必要冒上去
        swap(heap[ch],heap[fa]);//否则交换二值
        ch = fa;//更新
    }
}

堆结构实现

Java定义堆结构

    PriorityQueue<Integer> min = new PriorityQueue<>(); // 小顶堆
    PriorityQueue<Integer> max = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆

堆结构使用

  1. 295. 数据流的中位数
    题目:
    这道题要实现一个数据结构,用来保存一个数据流,支持向数据流中插入数据和查询数据流中的中位数,操作数量是50000次。
    分析
    如果是插入之后排序,假设插入和查询是1:1,那么每次排序的复杂度是n*log n ,就是25000 * 16 * 25000 ,时间复杂度过高。
    如果我们把这段数据流分为两段,一段保存排序后的左半部分,一半保存排序后的右半部分,例如 1,2 ; 3,4 ,这个数据流的中位数就是 (2 + 3)/2。
    对于左半部分只需要知道左半部分的最大值,右半部分只需要知道最小值,则可以使用堆结构来维护最大最小即可。
    维护方法
  • 使用大顶堆来维护左半部分数据,使用小顶堆维护右半部分数据。
  • 插入时如果两个堆大小一致,则将数据放入小顶堆排序,之后将小顶堆的根节点放入大顶堆。
  • 如果两个堆大小不一致,则将数据放入大顶堆排序,之后将大顶堆的头部放入小顶堆。
  • 因为大顶堆(左边)中的数据全部小于小顶堆(右边)中的数据,而新插入的数据可能会小于小顶堆(左边)中的数据,所以只需要将数据放入左边序,之后取出小的数据块中的最大值,放入右边的数据块中即可
    代码
class MedianFinder {
    Queue<Integer> A, B;
    public MedianFinder() {
        A = new PriorityQueue<>(); // 小顶堆,保存较大的一半
        B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半
    }
    public void addNum(int num) {
        if(A.size() != B.size()) {
            A.add(num);
            B.add(A.poll());
        } else {
            B.add(num);
            A.add(B.poll());
        }
    }
    public double findMedian() {
        return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
    }
}

posted @ 2022-04-13 11:24  xyee  阅读(109)  评论(0编辑  收藏  举报