堆的概念
堆排序是一种树形选择排序,在排序过程中,将待排序的记录看成一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序的序列中选出关键字最大或最小的记录。
堆分为大堆和小堆,大堆就是根节点比所有结点要大的完全二叉树,小堆就是跟结点要比所有结点要小的完全二叉树。
堆的时间复杂度为O(n*logn),堆是一个不稳定排序。
实现堆排序需要解决一下问题:
1.如何将一个无序序列建成一个堆
2.去掉堆顶元素,在堆顶元素改变之后,如何调整剩余元素成为一个新的堆。
void down(elem* a,int size, int parent)//向下调整
{
assert(a);
int child = 2 * parent + 1;
//运用假设法,假设左孩子小
while (child < size)//下标刚好停在数组末尾
{
if (child+1 < size && a[child + 1] < a[child])//如果该节点存在右节点并且child+1比child小时运行:child++
{
child++;//如果右孩子比左孩子大时让child+1,让较大的那个子节点与父节点进行比较
}
if (a[child] < a[parent])//如果子节点比父节点小,那就进行交换,然后继续向下调整
{
swap(&a[child], &a[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void sort2(elem* a, int x)//堆向下排序
{
int i = 0;
for (i = (x - 1 - 1) / 2; i >= 0; i--)
{
down(a,x,i);
}
int end = x - 1;
while (end > 0)
{
swap(&a[0], &a[end]);
down(a, end, 0);
end--;
}
}
降序需要建小堆,升序建大堆 ,这里作者的堆排序为降序,建小堆。
堆排序需要从第一个非叶子结点开始进行排序,最后一个结点的小标是x-1,所以其父亲的下标是(x-1-1)/2,第一个循环由此开始。
向下调整函数
down函数的作用使建小堆。
随后用down函数进行向下调整,down使用了假设法,开始时假设左孩子小。
使用while循环,循环结束时下标刚好停在数组末尾。
第一个if循环中的child+1<size是证明此结点是否存在右节点,且左孩子比右孩子小的是时候令child++使较大的结点与父亲结点进行比较。
如果子节点比父亲结点小,那就令二者进行交换,跟新二者的索引,随后继续向下调整。
就此小堆建造完成,解决第一个问题
while内容
到while循环时此时就建成了小堆。
第二个while循环使end到堆顶的下标时停下。此时交换第一个和最后一个的数据,此时最后一个元素就是最小的。
然后再进行向下调整排序,此时down的范围将排除最后一个元素,只从倒数第二个元素开始排序,此时最后一个元素的顺序已经固定了下来。
在while循环的作用下,小的数据不断被交换到数组后面,end--避免了以排好的数再次进行排序。
最后数组便排成了降序。