堆排序
堆排序基本思想
堆排序是一种使用堆进行排序的排序算法,堆:接近完全二叉树的结构,并且它的每一个子节点都大于(或小于)父节点。每一个子节点都小于父节点的被称为大堆,反之,称作小堆。
在数组中的存放为:
堆排序步骤:
- 首先将数组中的数据通过向下调整变为大/小顶堆。
- 交换堆顶和堆尾数据,将堆尾数据除外后,对堆顶向下调整重新调整为大/小顶堆。
- 重复操作2,直到堆中数据只剩一个。
排顺序时,使用大顶堆,这样保证每次交换到堆尾的数据都是当前堆中最大的值。相反如果排逆序,则使用小顶堆,保证每次交换到堆尾的数据都是堆中最小值。
如何构建大/小顶堆:
首先要找到二叉树中的最后一个非叶子节点,即下标为(n-2)/2的节点。之后从它开始逐个向前进行向下调整,直到调整到根节点。
代码如下:
int i = 0;
for (i = (n - 2) / 2; i >= 0; --i)
{
HeapAdjustDown(a, n, i);
}
排序部分:
代码如下:
int end = n - 1;
while (end > 0)
{
swap(&a[0], &a[end]);
HeapAdjustDown(a,end,0);
end--;
}
完整代码:
void HeapAdjustDown(int* a, int n, int father)
{
int child = father * 2 + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1]< a[child])
{
++child;
}
if (a[child] < a[father])
{
swap(&a[child], &a[father]);
father = child;
child = father * 2 + 1;
}
else
{
break;
}
}
}
void HeapSort(int* a, int n)
{
int i = 0;
for (i = (n - 2) / 2; i >= 0; --i)
{
HeapAdjustDown(a, n, i);
}
int end = n - 1;
while (end > 0)
{
swap(&a[0], &a[end]);
HeapAdjustDown(a,end,0);
end--;
}
}
堆排序效率分析
堆排序是一种不稳定的排序,时间复杂度为O(nlogn)。
堆排序分为两个部分,初始化堆和排序重建堆,初始化堆的时间复杂度为O(N),排序重建的复杂度为O(NlogN),所以整个堆排序过程的时间复杂度为O(nlogn)。
堆排序在初始数据本就是堆的情况下,比较次数减少,不需要构建堆,效率较高。