一、算法描述
(二叉)堆数据结构是一种数组对象,可以被视为一棵完全二叉树。树中每个结点与数组中存放该结点值的那个元素对应。树的每一层都是填满的,最后一层除外(最后一层从一个结点的左子树开始填)。(二叉)堆 有两种:最大堆和最小堆。
最大堆:父结点的键值总是大于或等于任何一个子节点的键值,A[Parent(i)] >= A[i]。
最小堆:父结点的键值总是小于或等于任何一个子节点的键值,A[Parent(i)] <= A[i]。
堆可以被看成是一棵树,结点在堆中的高度敌营为本结点到叶子的最长简单下降路径上边的数目;定义堆的高度为树根的高度。
二、保持堆的性质
MAX-HEAPIFY是对最大堆进行操作的子程序。其输入为一个输入A和下标i。MAX-HEAPIFY让A[i]在最大堆中“下降”,使i为根的子树成为最大堆。
MAX-HEAPIFY(A, i)
l <- LEFT(i)
r <- RIGHT(i)
if l <= heap-size[A] and A[l] > A[i]
then largest <- l
else largest <- i
if r <= heap-size[A] and A[r] > A[largest]
then largest <- r
if largest != i
then exchange A[i] <-> A[largest]
MAX-HEAPIFY(A, Largest)
heap-size[A]是存放在A中的堆的元素个数,LEFT(i) 左子节点,RIGHT(i) 右子节点。
void swap(int *i, int *j)
{
int temp = *i;
*i = *j;
*j = temp;
}
void MaxHeapify(int arg[], int i, int size)
{
int LeftChild = 2 * i;
int RightChild = 2 * i + 1;
int largest = i;
if( (LeftChild <= size) && (arg[LeftChild] > arg[largest]) )
{
largest = LeftChild;
}
if( (RightChild <= size) && (arg[RightChild] > arg[largest]) )
{
largest = RightChild;
}
if( largest != i )
{
swap(&arg[i], &arg[largest]);
MaxHeapify(arg, largest, size);
}
}
保持堆性质的另一种C语言实现方法,参考《数据结构》。
void MaxHeapify(int arg[], int i, int size)
{
int nChild = 0;
int tmp = arg[i];
for (nChild = 2 * i; nChild <= size; nChild *= 2)
{
if ((nChild < size) && (arg[nChild + 1] > arg[nChild]))
{
++nChild;
}
if (tmp > arg[nChild])
{
break;
}
arg[i] = arg[nChild];
i = nChild;
}
arg[i] = tmp;
}
三、建堆
BUILD-MAX-HEAP(A)
heap-size[A] <- length[A]
for i <- [Length[A] / 2] downto 1
do MAX-HEAPIFY(A, i)
给一个的数组建立最大堆。类似的,利用BUILD-MIN-HEAP来建立最小堆。建堆的目的就是将一个无序线性数组建成一个最大(小)堆。
void BuildMaxHeap(int arg[], int size)
{
int i;
for(i = (size / 2); i >= 1; i--)
{
MaxHeapify(arg, i, size);
}
}
四、堆排序算法
通过上面的算法,建立了一个最大堆。因为数组中最大元素的根A[1],则可以通过把它与A[n]互换来达到最终正确的位置。
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i <- length[A] downto 2
do exchange A[1]<->A[i]
heap-size[A] <- heap-size[A] - 1
MAX_HEAPIFY(A, 1)
如果从堆中“去掉”结点n(通过减小heap-size[A]),可以很容易地将A[1..(n-1)]建成最大堆。原来根的子女仍是最大堆,而新的根元素可能违背了最大堆性质。这时调用MAX-HEAPIFY(A, 1)就可以保持这一性质,在A[1..(n-1)]中构造出最大堆。堆排序算法不断重复这个过程,堆的大小由(n-1)一直降到2。
void HeapSort(int arg[], int size)
{
int i;
BuildMaxHeap(arg, size);
for(i = size; i >= 2; i--)
{
swap(&arg[1], &arg[i]);
MaxHeapify(arg, 1, i - 1);
}
}
堆排序方法对记录数较少的文件并不值得提倡,当对n较大的文件还是有效的。