堆排序
提到堆排序,那么我们就需要提到堆了,这里我就不仔细介绍堆了,我主要是为了讲一下堆是如何排序的。
首先,堆是分大堆和小堆的,在我们确定一个数组排升序或者降序的时候我们也需要根据这个确定是大堆或者是小堆,下面我将仔细介绍一下堆排序的方法,听完这个方法你就可以知道是如何根据升序或者降序确定大堆或小堆了。
堆的性质,我们这里以大堆为例,大堆就是根节点它比左右子树的节点的值都大,而小堆就是与其相反,继续说大堆,我们假如需要对一个数组进行升序排序,那么堆排序是如何做到排序的呢?
上面说了,大堆是将最大的数放在根节点上面,而我们知道这个堆是根据数组建立的,那么我们可以通过交换的方式,将一个最大的数通过大堆的方式放在堆顶,然后再将这个数和最后一个节点交换,然后再将最后这个节点暂时排除在外,然后再通过调整重新将堆变为大堆,此时再将堆顶的数和此时最后一个堆的节点交换,然后再将其排除在外,循环这个过程,那么我们就可以实现排序了。
下面我以图做一下简单的过程演示:
通过这个我们可以简单的先理解一下堆排序的这个过程,下面还是要以代码来讲解。
void AdJustDown(int* a, size_t n, size_t k)
{
assert(a);
int parent = k;
int child = 2 * k + 1;
while (child < n)
{
if (child + 1 < n && a[child + 1] > a[child])//判断左右孩子哪个大,然后得到较大的一个孩子
{
child++;
}
if (a[child] > a[parent])//(建大堆)如果孩子节点的值比父节点大,那么交换,向下进行调整,利用循环不断调整
{ //如果建小堆的话就将这里的大于改成小于就好
swap(a[child], a[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void HeapSort(int* a, size_t n)//堆排序,如果升序则需要大堆,将最大值放到堆顶,然后将其与最后一个叶子节点的值交换
{
assert(a);
//建堆
for (int i = (n - 2) / 2; i >= 0; --i)
{
AdJustDown(a, n, i);
}
//排序
int end = n - 1;//用end标记最后一个叶子节点
while (end > 0)
{
swap(a[0], a[end]);//每一次交换完,将堆顶的最大值和最后一个叶子节点的值交换后,
AdJustDown(a, end, 0); // 就将这个节点的值取出,然后再将其余的数重新调整
--end;
}
}
void TestHeapSort()
{
int a[] = { 2, 5, 4, 9, 3, 6, 8, 7, 1, 0 };
HeapSort(a, sizeof(a) / sizeof(a[0]));
Print(a, sizeof(a) / sizeof(a[0]));
}
在上面我已经将向下调整的过程讲过了,所以我现在要讲的是具体的排序过程。
看代码,n是数组的大小,所以n-1可以得到数组下标,然后我们知道二叉树有这样一个规律:父节点的位置乘以2加上1就是其左孩子的位置,乘以2后加上2就是其右孩子的位置,所以n-1再-1后得到的值除以2就可以得到当前位置的父节点的位置,然后我们就可以来利用循环实现这个不断向下调整的过程。
重点是下面的排序,我们可以通过一个end来标记最后一个节点,只要最后一个节点位置还大于0,我们就可以循环的实现交换并调整交换后的堆,end再减减一下就可以将最后一个节点排除在外,循环完成退出后就可以让其有序。
注:所有代码都是放在VS2013编译器下实现的。