数据结构与算法 - 堆

堆的一个经典的实现是完全二叉树(complete binary tree),这样实现的堆称为二叉堆(binary heap)。

这里来说明一下满二叉树的概念与完全二叉树的概念。

满二叉树:除了叶子节点,所有的节点的左右孩子都不为空,就是一棵满二叉树,如下图。

image

可以看出:满二叉树所有的节点都拥有左孩子,又拥有右孩子。

完全二叉树:不一定是一个满二叉树,但它不满的那部分一定在右下侧,如下图

image

堆的特性:

  • 必须是完全二叉树
  • 任一结点的值是其子树所有结点的最大值(大顶堆)或最小值(小顶堆)

image

最小堆的插入(ADD)

image

假设现有元素 5 需要插入,为了维持完全二叉树的特性,新插入的元素一定是放在结点 6 的右子树;同时为了满足任一结点的值要小于左右子树的值这一特性,新插入的元素要和其父结点作比较,如果比父结点小,就要把父结点拉下来顶替当前结点的位置,自己则依次不断向上寻找,找到比自己大的父结点就拉下来,直到没有符合条件的值为止。

动画讲解

  1. 在这里先将元素 5 插入到末尾,即放在结点 6 的右子树。
  2. 然后与父类比较, 6 > 5 ,父类数字大于子类数字,子类与父类交换。
  3. 重复此操作,直到不发生替换。

最小堆的删除(DELETE)

image

核心点:将最后一个元素填充到堆顶,然后不断的下沉这个元素。

假设要从节点 1 ,也可以称为取出节点 1 ,为了维持完全二叉树的特性 ,我们将最后一个元素 6 去替代这个 1 ;然后比较 1 和其子树的大小关系,如果比左右子树大(如果存在的话),就要从左右子树中找一个较小的值替换它,而它能自己就要跑到对应子树的位置,再次循环这种操作,直到没有子树比它小。

通过这样的操作,堆依然是堆,总结一下:

  1. 找到要删除的节点(取出的节点)在数组中的位置
  2. 用数组中最后一个元素替代这个位置的元素
  3. 当前位置和其左右子树比较,保证符合最小堆的节点间规则
  4. 删除最后一个元素

时间复杂度

对于有 n 个节点的堆来说,其高度 \(d = \log_2{n} + 1\)。 根为第 0 层,则第 \(i\) 层结点个数为 \(2i\)

考虑一个元素在堆中向下移动的距离。

  1. 大约一半的结点深度为 \(d-1\) ,不移动(叶)。
  2. 四分之一的结点深度为 \(d-2\) ,而它们至多能向下移动一层。
  3. 树中每向上一层,结点的数目为前一层的一半,而子树高度加一

堆有 \(\log n\)层深,所以插入删除的平均时间和最差时间都是 \(O(\log N)\)

优先队列

普通队列是一种先进先出的数据结构,先放进队列的元素取值时优先被取出来。而优先队列是一种具有最高优先级元素先出的数据结构,比如每次取值都取最大的元素。

优先队列支持下面的操作:

  1. 找出优先级最高的元素(最大或最小元素);
  2. 删除一个具有最高优先级的元素;
  3. 添加一个元素到集合中。
posted @ 2022-03-01 16:02  Logan_Xu  阅读(36)  评论(0编辑  收藏  举报