基本思想
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
过程分析
我们先将上面的数字按照堆的形式展现出来:
-
以最后一棵小子树开始构建大堆,也就是下面蓝框选中的部分
把31拿出来和子树比较,因为该树只有左子树,所以只需要和左子树进行比较,发现1比31小,所以无需交换,然后换至下一棵树。 -
因为是倒叙构建才能不断把数字大的往上浮,所以下一棵树是以3号下标为根的子树,也就是下面蓝框选中的部分。
把45拿出来和下面的子树进行比较,左子树是5,右子树是47,比较大的是47,我们用47和45进行比较,发现3号下标的右子树大于该数字,所以47放到3号下标,45放到以8号为根的子树中去找自己的定位,因为8号下标已经没有子树了,所以就只能将45放到8号位置。
-
下来我们该以2号下标为根的树了,比较完成后没有改变
-
下来继续根为1号下标的树
因为47和31较大的是47,我们用47和3进行比较 ,47比3大,所以把47放到1号下标的位置,现在的状态是这样的。
本来3是要放在3号下标的,但是因为3号下标下面还有子树,所以不能放到3号下标,继续进行比较,5和45大的是45,45和3比较,45大,所以把45放到3号位置。
temp继续向下比较,8号位置没有子树了,所以就把3放到8号位置
这样以1号为根的树就完成了大堆的构建,下来是0号,同理最终构建得出的大堆是
到这里,我们的大堆构建过程已经完成了。 -
通过大堆我们可以得到这些数字中的最大数字,也就是现在处于0号下标的47,我们把47和最后9号下标的1进行交换,这样我们就得到了排序的最后面的一个数字。
然后我们把9号位置屏蔽掉,在0号到8号里面再次构建大堆。 -
构建完我们就在这些数中又可以得到堆顶最大的数字,如下图
-
这次我们继续吧最大的45扔到最后,也就是和8号位置的3进行交换,交换完是这样子的
-
依次类推,这样就得到了有序的序列,也就排序完成了。
代码实现
void Swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
void AdJustDown(int* arr, int n, int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && arr[child] < arr[child + 1])
{
++child;
}
if (arr[parent] < arr[child])
{
Swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void HeapSort(int* arr, int n)
{
//排升序,建大堆
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdJustDown(arr, n, i);
}
int end = n - 1;
while (end > 0)
{
//把堆顶当前最大的数依次换到最后
Swap(&arr[0], &arr[end]);
AdJustDown(arr, end, 0);
--end;
}
}
测试用例:
void Test()
{
int arr[] = { 4, 3, 15, 45, 31, 14, 9, 5, 47, 1 };
int length = sizeof(arr) / sizeof(arr[0]);
HeapSort(arr, length);
for (int i = 0; i < length; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
特性总结
- 堆排序使用堆来选数,效率就高了很多。
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 稳定性:不稳定