6.1 认识堆
1.(二叉)堆是一个数组,他可以被看成一个近似的完全二叉树。表示堆的数组A包括两个属性:A.Length给出数组元素的个数(数组的长度,有的元素中可能没有存值),A.heap-size表示有多少个堆元素存储在该数组中。这里0=<A.heap-size=<A.Length:就是说,虽然A[1..A.Length]可能都存有数据,但只有A[1..A.heap-size]中存放的时堆中的有效数据。
2.树的根节点是A[1]。这样给定一个节点下表 i ,很容易表示其父节点、左孩子、右孩子分别为:return A[i/2] 向下取整、return 2*i、return 2*i + 1.
3.堆氛围最大堆和最小堆。最大堆中除了根节点外,所有的元素都要满足:A[PARENT(i)]>=A[i];最小堆正好相反,除了根节点外多有节点 i 都有A[PARENT(i)] =< A[i]。在排序中我们使用的是最大堆。最小堆通常用于构造优先队列。
4.树的高度定义为根节点到叶节点最长简单路径上边的数目;所以一个节点的高度为该节点到叶子节点最长简单路径上边的数目。该堆的高度为lgN。堆上的一些基本操作至多与树的高度成正比,即时间复杂度为LgN。
6.2 维护堆的性质
1.MAX-HEAPIFY过程是用于维护最大堆性质的重要过程。他的输入为一个数组A和一个下表 i ,期中 i 表示新节点插入堆的位置。调用的时候,我们假定根节点的LEFT(I)、RIGHT(I)的二叉树都是最大堆,但A[i]有可能小于其孩子,这就违背了最大堆的性质。此时我们让A[i] 逐层下降,来维护最大堆的性质。
2.MAX-HEAPIFY过程的伪代码如下:时间复杂度为LgN,即树的高度 h
MAX-HEAPIFY(A,i )
1,l = LEFT(i)
2,r = RIGHT(i)
3,if l =< A.heap-size and A[l] >A[i] //先和左孩子节点比较大小,注意到叶子节点截止
largest = l
else largest = i
4,if r=< A.heap-size and A[r]>A[largest]//与右孩子节点比较
largest = r
5,if largest is not equal i //largest 的值不是 i 说明A[i] 破坏了最大堆的性质,交换并递归调用MAX-HEAPIFY(A,i)
exchange A[i] with A[largest]
MAX-HEAPIFY(A,largest)
6.3 建堆 时间复杂度O(h)
一个拥有n个节点的堆,画出其完全二叉树后知道,子数组A[n/2 + 1..n]中的元素都是树的叶子节点,所以我们不需要维护。下面通过BUILD-MAX-HEAP对树中的其他节点都调用一次MAX-HEAPIFY,既可以得到一个最大堆(通过一步步维护堆的性质,来建堆)
BUILD-MAX-HEAP(A)
1,A.heap-size = A.length
2,for i = A.length/2 downto 1 //从第一个不是叶子节点的节点开始,进行建堆(即逐步维护)。刚开始看成是一个最大堆,然后再一个一个的来维护
MAX-HEAPIFY(A,i)
6.4 通过前面的建堆,现在我们可以在此基础上进行排序。即构建最小堆。构建好的最小堆就是我们想要的从小到大的排序序列。 时间复杂度 O(nlgn)
HEAPSORT(A)
1, BUILD-MAX-HEAP(A)
2, for i=A.lenght downto 2
2,exchange A[1] with A[i]//将最大堆上面最后一个元素和对顶元素进行交换,逐步的交换
4,A.heap-size = A.heap-size -1
5, MAX-HEAPIFY(A,1) //因为每次都是讲堆顶元素换成新的数据,有可能破坏了最大堆的性质,所以我们需要进行维护。
至此,我们通过建最大堆,然后通过虚拟的摘堆,就可以将其进行堆排序!