二叉堆

来源:互联网 发布:qt usb通信 linux api 编辑:程序博客网 时间:2024/06/06 09:57

堆结构定义

堆是一种树型结构,对于堆结构来说,堆的每一个节点的元素都要大于(或小于)其所有子节点的元素,满足这一条件的树被称为堆。若每个节点元素大于其所有子节点的元素,则这个堆被称为大根堆(最大堆),反之若每个节点元素小于其所有子节点的元素,则被称为小根堆(最小堆)。

二叉堆是每个节点最多只有两个子节点的堆,它可以被看成一个近似地完全二叉树,可以用数组表示。二叉堆除了最底层外,该树是完全充满的,而且是从左向右填充。如图所示,左边是一个大根堆的树型表示,右边是数组表示。


在数组实现二叉堆中,我们用长度为N+1的数组来表示一个大小为N的二叉堆,数组的第一个元素(即下标为0的元素)不会被使用,堆元素存放在数组下标为1~N的地方。在一个二叉堆中,位置k的节点的父节点的位置为[k/2],而它的两个子节点的位置则分别为2k和2k+1。
大根堆通常在堆排序中被使用,小根堆则通常用于构造优先队列。维护一个堆结构需要建堆和堆有序化两个基本步骤。

堆有序化(HEAPIFY)

堆有序化是维护一个堆的性质的重要过程。堆有序化分为自底向上和自顶向下两种。

自底向上的堆有序化(上浮)

如果堆的有序状态因为某个节点变得比它的父节点更大而被打破,那么我们就需要通过交换它和它的父节点来修复堆。交换后,这个节点比它的两个子节点都大(一个是曾经的父节点,另一个比它更小,因为它是曾经的父节点的子节点),但这个节点仍然可能比它现在的父节点大,因此我们需要一遍遍重复这个步骤让这个节点不断向上移动到我直到遇到比它大的父节点,从而使堆恢复秩序。


伪码如下:

HEAPIFY_FLOAT(k)
while k>1 and (k>k/2)
swap(k,k/2)
k = k/2
源码

void Heapify_Float (int *a, int k){while (k > 1 && k/2 < k){swap (a[k/2], a[k]);k = k/2;}}


自顶向下的堆有序化(下沉)

与自底向上的堆有序化原理相同,但方向相反。


源码
void Heapify_Sink (int *a, int k){while (2*k <= N){int i = 2*k;if (i < N && i < i+1)i++;if (k > i)break;swap (a[k], a[i]);}}


建堆(BUILD_HEAP)

我们可以用自底向上或者自顶向下的方法利用过程HEAPIFY来把一个大小为N的数组转换为最大堆。每个节点都可以看成是一个只包含一个元素的堆,过程BUILD_HEAP对树中的其它节点都调用一个HEAPIFY,伪码如下:
//以构建大根堆为例
BUILD_DEAP(A)
for i = [A.length/2] to 1
HEAPIFY(A,i)
建堆主要包括三个过程:
·初始化:在第一次循环迭代前,i=[n/2],而[n/2+1], [n/2]+2, …, n都是叶节点,因而是平凡大根堆的根节点。
·保持:为了看到每次迭代都维护这个不循环变量,注意到节点i的子节点的下标均比i大。所以根据循环不变量,它们都是最大堆的根。这也是调用HEAPIFY(A,i)使节点i成为一个最大堆的根的先决条件。而且,过程HEAPIFY维护了节点i+1,i+2,… , n都是一个最大堆的根节点的性质。在for循环中递减i的值,为下一次循环重新建立循环不变量。
·终止:过程终止时,i=0。根据循环不变量,每个节点1, 2, … , n都是一个最大堆的根。特别的是,节点1就是最大的那个堆的根节点。
源码

void BuildHeap (int *a, int N){int i = N/2;while (i > 0){Heapify_Float (a, i);i--;}}




本文部分内容摘自《算法导论(第3版)》和《算法(第4版)》