现有一个数组arr,我们需要按升序来对它进行排序。
arr[]={9,1,3,5,7,2,4,6,8}
在建堆之前,我们需要先考虑一个问题--是建大堆还是建小堆,以及建堆时需要向上调整还是向下调整?
首先我们假设建大堆,而建大堆又分为两种情况:
如果是向上调整的话只需要我们从第二个元素开始,依次进行向上调整,直至最后一个元素。
若是选择向下调整的话,只需要从最后一个元素的父节点开始即可。
因为向上调整的时间复杂度为O(N*logN),而向下调整的复杂度为O(N),所以我们较多使用向下调整。
从上面两图可以看出,虽然堆只帮我们排了一部分,但是堆帮我们把最大值放在了根的位置,所以我们可以将最大值放与数组末尾的值进行交换,这样最大值就在正确的位置了,之后就不需要再动这个值,所以让size--,并再进行向下调整,找到次大值,重复此过程直至完成排序。
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void AdjustDwon(int* a, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size && a[child + 1] > a[child])
{
++child;
}
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = child * 2 + 1;
}
else
{
break;
}
}
}
void AdjustUP(int* a, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (a[parent] < a[child])
{
swap(&a[parent], &a[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void heapsort(int* a, int size)
{
for (int i = 1; i < size; ++i)
{
AdjustUP(a, i);
}
for (int i= ((size - 1) - 1) / 2; i >=0 ; --i)
{
AdjustDwon(a, size, i);
}
int end = size - 1;
while (end > 0)
{
swap(&a[0], &a[end]);
AdjustDwon(a, end, 0);
--end;
}
}
再来看小堆排序的结果,我们只能知道根是最小的值,但是不知道最次小值,还需要再建一个堆来选数,使得时间复杂度达到了O(N)。
因此,升序排列建大堆,降序排列建小堆。