二叉堆(Binary Heap)

二叉堆(Binary Heap)

二叉堆(Binary Heap)其实比较简单,但却非常有用,常见的应用二叉堆排序和优先级队列。本文将介绍二叉堆的基本性质、基本操作和二叉堆在优先级队列上的应用。

二叉堆概述

二叉堆其实就是一个完全二叉树(完全二叉树的介绍见二叉树(Binary Tree):先序遍历、中序遍历、后序遍历和层次遍历),只不过用数组存储形式。
在这里插入图片描述

这里特意保留arr[0]不用,这样可以方便地计算父亲和儿子节点的index。因此,arr[1]是二叉树的根节点。二叉堆分为最大堆和最小堆两种:

  • 最大堆:每个节点的值都大于等于它的两个子节点。
  • 最小堆:每个节点的值都小于等于它的两个子节点。

显然,最大堆最大的元素就是其堆顶arr[1],最小堆最小的元素就是其堆顶arr[1]。

优先级队列

优先级队列(Priority Queue)在插入或者删除元素后,会自动完成顺序更新,这种顺序的更新是使用了二叉堆来完成的。优先级队列最重要的操作就是插入和删除(使用最大堆就是delMax,使用最小堆就是delMin),主要接口如下:

template <typename T>  // 元素类型
class MaxPriQueue {
  private:
    T* pq
    int num = 0;
    
  public:
    MaxPriQueue(int n) {
      pq = new T[n + 1];
    }
    void insert(T value);
    T delMax()
  private:
    int parent(int root) {
      return root / 2;
    }
    int left(int root) {
      return root * 2;
    }
    int right(int root) {
      return root * 2 + 1;
    }
    T max() {
      return pq[1];
    }
    void swap(int i, int j) {
      T tmp = pq[i];
      pq[i] = pq[j];
      pq[j] = tmp;
    }
    bool less(int i, int j) {
      return pq[i] < pq[j];
    }
    void swim(int x);  // 上浮第x个元素
    void sink(int x);  // 下沉第x个元素
}

二叉堆最重要的两个操作就是上浮(swim)和下沉(sink),二叉堆就是靠这两个方法维护其基本性质。这里都以以最大堆为例,最小堆情况是类似的。当我们在二叉堆里插入或者删除元素时候,肯定会破坏二叉堆的性质。当某个父节点元素小于其子节点元素时,就需要将其与子节点进行交换,也就是下沉。反之,就需要上浮。

节点的上浮可能需要多次才能到达正确的位置,代码如下:

void swim(int x) {
  while(x > 1 && less(parent(x), x)) {
    sawp(parent(x), x);
    x = parent(x);
  }
}

节点A下沉需要比较两个子节点,只要比其中之一小就需要下沉,并且将较大的子节点与A节点交换。代码如下:

void sink(int x) {
  while(left(x) <= num) {
    int maxIndex = left(x);
    if(right(x) <= num && less(maxIndex, right(x))) {
      maxIndex = right(x);
    }
    if(less(maxIndex, x)) break;
    swap(x, maxIndex);
    x = maxIndex;
  }
}

有了上浮和下沉两个方法,优先级队列的insert和delMax就比较容易了。insert就是将元素插入数组尾部,然后再执行上浮到正确位置即可。

void insert(T value) {
  num++;
  pq[num] = value;
  swim(num);
}

delMax只需要将堆顶元素A和堆底元素B交换,然后删除堆底的A,再将堆顶的B下沉到正确位置即可。

T delMax() {
  T max = pq[1];
  swap(1, num);
  pq[num] = nullptr;
  num--;
  sink(1);
  return max;
}

二叉堆就是一棵完全二叉树,非常适合使用数组存储,而二叉堆的性质主要就是靠上浮和下沉来维护。

C++标准库有优先级队列可直接使用:

template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>
> class priority_queue;

详情戳这里。优先级队列的应用可以刷一下力扣合并 K 个升序链表

参考:

  • https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-daeca/er-cha-dui-1a386/
  • https://en.cppreference.com/w/cpp/container/priority_queue
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值