记录以小根堆为例的堆排序。
介绍
堆是一棵完全二叉树。我们要维护的性质是:对于每个节点,其孩子节点的值大于等于其本身的值。
技巧
1.根节点的下标为1而不是0。这样做的好处是:若父节点下标为i,可以保证左孩子下标为2*i,右孩子下标为2*i+1。这是使用堆时约定的表示。
2.利用下标为0的节点设置哨兵,比堆中所有元素都小。可以简化插入元素时的循环的判定条件。
实现
1. 数据结构
常用数组作为实现堆的数据结构。
typedef int ElemType;
typedef struct
{
ElemType Elements[100];
int size;
}n, *PriortyQueue;
2. 插入元素
插在当前堆的末尾,并通过++H->size维护堆尺寸。然后进行向上调整,新节点不断与其父节点交换,直到父节点的值小于等于新节点的值,停止。
// 插入一个数据
void push_back(ElemType key, PriortyQueue H)
{
int i;
for(i = ++H->size ; H->Elements[i/2]>key ; i = i/2)
H->Elements[i] = H->Elements[i/2];
H->Elements[i] = key;
}
3. 取出元素
将堆最后一个元素调整为根节点,并通过H->size–维护堆尺寸。然后进行向下调整,根节点不断与其值较小的孩子节点交换,直到没有孩子节点或满足堆性质时停止。
// 取出堆顶数据
ElemType pop(PriortyQueue H)
{
ElemType minElem = H->Elements[1];
ElemType lastElem = H->Elements[H->size--];
int i=1;
while(2*i <= H->size && lastElem > H->Elements[2*i] || 2*i+1 <= H->size && lastElem > H->Elements[2*i+1])
{
int j=2*i;
//取儿子中更小的那一个
if(j+1<=H->size && H->Elements[j]>H->Elements[j+1])
j = j+1;
// 儿子取代父亲,并寻找父亲下一个候选位置
H->Elements[i] = H->Elements[j];
i = j;
}
H->Elements[i] = lastElem;
return minElem;
}