堆、priority_queue

堆(heap)

  • 概念

    堆是一颗有着特殊性质的完全二叉树。对于树的每个结点,如果存在子树,那么该结点的权值大于等于(或者小于等于)子树中所有结点的权值

    如果根结点的权值大于等于子树的权值,称为大根堆;反之称为小根堆

    image-20260315165244392

  • 堆的存储

    由于堆是一颗完全二叉树,故堆可以使用一个数组来存储(顺序存储)。顺序存储时,利用满二叉树的特性来计算父子结点关系

    注:

    • 存储虽然简单,但题目不会这么好心。一般给我们的是一组树,按照这组树还原成二叉树后,并不是堆结构
    • 我们有两种做法:用数组存下这组树,再调整成堆;或者创建一个堆,把这组树插入到堆中
  • 练习(判断堆)

    image-20260315165426539

    注:图5不是一颗完全二叉树,故不是堆


堆的核心操作

  • 概念

    堆中的所有运算,比如建堆、插入或删除元素,都是基于堆的两个核心操作实现的:

    • 向上调整算法
    • 向下调整算法
  • 堆的常用存储结构

    如果将满二叉树,按照层序遍历的过程编号,那么

    结点 \(i\) 的左孩子编号:为 \(2i\)

    结点 \(i\) 的右孩子编号:为 \(2i + 1\)

    结点 \(i\) 的双亲编号:\(i/2\)

    注:若以 0 开始编号,则

    结点 \(i\) 的左孩子编号:为 \(2(i + 1) - 1=2i+1\)

    结点 \(i\) 的右孩子编号:为 \(2(i + 1) + 1 - 1 = 2i + 2\)

    结点 \(i\) 的双亲编号:\((i + 1)/2-1\)

  • 向上调整算法(siftUp)

    向上调整算法,用于向堆中插入元素

    当堆中新来一个元素,放在末尾时,从这个结点开始逐渐向上调整(将该点与父结点的权值作比较,若比父节点大,则交换)。重复比较操作,直到小于等于父节点的权值,或换到根结点为止

    时间复杂度为 \(O(log(N))\)

  • 向下调整算法(heapify)

    向下调整算法,用于删除堆顶元素,或者堆排序中的建堆操作

    向下调整,就是从这个结点开始,逐渐向下调整。找出左右孩子中权值最大的那个,如果该点的权值比权值大的孩子小,就交换。重复比较交换操作,直到该点比两个孩子结点的权值都大,或者换到叶子结点为止

    时间复杂度为 \(O(N)\)

    一般都是先把堆最后一个元素和目标结点交换,再对该点进行向下调整算法,则达到删除堆元素的目的


堆的模拟实现(大根堆)

  • 代码实现

    #include <iostream>
    
    using namespace std;
    
    const int N = 1e6 + 10;
    int n, heap[N];
    
    // 向上调整算法
    void up(int child) {
        int parent = child / 2;
        while (parent >= 1 && heap[child] > heap[parent]) {
            swap(heap[child], heap[parent]);
            child = parent;
            parent = child / 2;
        }
    }
    
    // 向下调整算法
    void down(int parent) {
        int left_child = parent * 2;
    
        // 若左孩子不存在,则右孩子必定不存在
        while (left_child <= n) {
            int right_child = left_child + 1;
            int max_of_child = left_child;
            if (right_child < n && heap[right_child] > heap[left_child]) {
                max_of_child = right_child;
            }
            if (heap[parent] >= heap[max_of_child]) {
                return;
            }
            swap(heap[parent], heap[max_of_child]);
            parent = max_of_child;
            left_child = parent * 2;
        }
    }
    
    // 插入元素
    void push(int data) {
        heap[++n] = data;
        up(n);
    }
    
    // 删除堆顶元素(前提堆有元素)
    void pop() {
        swap(heap[1], heap[n]); // 交换到堆末尾
        n--; // 删除堆尾
        down(1);
    }
    
    // 查询堆顶元素(前提堆有元素)
    int top() {
        return heap[1];
    }
    
    // 堆的大小
    int size() {
        return n;
    }
    
    int main() {
        // 建堆
        int a[10] = {1, 41, 23, 10, 11, 2, -1, 99, 14, 0};
        for (int i = 0; i < 10; ++i) {
            push(a[i]);
        }
    
        while (size()) {
            cout << top() << " ";
            pop();
        }
    
        return 0;
    }
« 二叉树 ← 返回列表 二叉搜索树(BST) »