给出数组A[1...n],我们可以将其中的元素用下面的方法以非降序有效地排序。
首先将A变换成堆,并使其具有这样的性质:每个元素的键值是该元素本身,即key(A[i])=A[i],1 ≤ i ≤ n。
转换成堆之后,由于A中各项的最大值存储在A[1]中,可以将A[1]和A[n]交换,使得A[n]是数组中的最大元素。这时A[1]中的元素可能小于存放在它的一个子节点中的元素,于是用过程Sift-down将A[1...n-1]转换成堆。接下来将A[1]和A[n-1]交换,并调整数组A[1...n-2]成为堆。交换元素和调整堆的过程一直重复,直到堆的大小变成1为止,这时,A[1]是最小的。
过程 HeapSort
输入 n个元素的数组A[1...n]
输出 以非降序排列的数组A
算法描述
for j ← n downto 2
互换 A[1]和A[j]
Sift-down(A[1...j-1], 1)
end for
注意这里算法描述的数组的索引都是1...n,而不是大家习惯的0...n-1。
创建堆需要Θ(n)的时间,Sift-down运算需要O(log n)时间,并且要重复n-1次,也就是用该算法给n个元素排序需要时间O(n log n)。
// heap[0]用来存储数组中堆数据的长度,堆数据heap[1]...heap[heapLength]
// 所以数组的实际长度是heapLength+1,我们只对从数组索引为1开始的heapLength个数据进行操作
void siftDown(int* heap, int siftDownTo, int index)
{
if (index < 1 || index * 2 > siftDownTo)
return;
bool done = false;
while(!done && index * 2 <= siftDownTo)
{
index *= 2;
if (index + 1 <= siftDownTo && heap[index + 1] > heap[index])
index += 1;
if (heap[index] > heap[index / 2])
{
int temp = heap[index];
heap[index] = heap[index / 2];
heap[index / 2] = temp;
}
else
{
done = true;
}
}
}
void heapSort(int* heap)
{
if (heap == NULL)
return;
int heapLength = heap[0];
int temp = 0;
for (int i = heapLength; i >= 2; i--)
{
temp = heap[1];
heap[1] = heap[i];
heap[i] = temp;
siftDown(heap, i - 1, 1);
}
}
我使用的数组是这样定义的:
const int HEAP_LENGH = 10;
int heap[HEAP_LENGH + 1] = { HEAP_LENGH, 20, 11, 9, 10, 5, 4, 5, 3, 7, 3 };
heapSort(heap);
更多内容请参考: