堆排序:虽然用到类似二叉树的思想,但是仍是原地排序。堆分为大根堆和小根堆两种。堆顶元素一定是该序列最值。对于从小到大的排序,适合采用大根堆,每轮都将堆顶的最大值与堆末元素交换。
堆排序的基本思想:由于大根堆的堆顶元素是最大值,因此我每次都可以找到当时的最大值,从而达到排序的目的。具体的,先把原始序列变成堆,然后把堆顶最大值调换到尾部作为已排好序的部分(堆外),此时堆变小且被破坏,要对其调整,重复执行这一过程。
注意:由小到大排序的时候,使用大根堆,这样每次将最大的数交换到末尾,达到由小到大排序的目的。由大到小排序的时候,使用小根堆。
堆的调整过程:调整的方向是从根向下到叶子的方向。保证在这一子树中,较大的元素留在父节点。
这个过程是堆排序最关键的操作,一般写为一个sink函数:找较大孩子,若较大孩子比父节点大,就交换,然后进其较大孩子那侧继续。
堆的初建过程:为了保证整个序列满足堆的性质,即要所有子树都满足堆的性质。要从最后一个子树开始进行调整过程。
单独一个节点的子树必然满足堆的性质,所以要从最后一个非单独节点的子树(即,最后一个非叶子节点)开始进行调整过程。
堆排序有两种方式:递归方式和非递归方式。其整体思路一致,区别仅在于调整过程的形式:只不过递归方式是递归的形式来遍历和调整子树,非递归方式是用循环的形式来遍历和调整子树。
递归形式代码:
- //参数:堆起始,调整元素位置,当前堆大小
- void sink(int a[], int i, int n)
- {
- int lc = 2*i;
- if (lc > n)
- return; //已到叶子节点
- int rc = lc + 1;
- int mc = (rc > n)? lc : ((a[lc]>a[rc])?lc:rc);
- //mc为较大的孩子
- if(a[i] >= a[mc])
- return; //此时子树不会有变化,无需继续调整
- swap(&a[i], &a[mc]);//较大的孩子对应的子树发生变化
- sink(a, mc, n);
- }
- void heap_sort_recursion(int a[], int n)
- {
- int i;
- for(i=n/2;i>=1;i--) //建堆过程:从最后一个非叶子节点到根节点,都要做sink
- sink(a, i, n);
- for(i=1;i<=n;i++)
- {
- swap(&a[1],&a[n-i+1]); //逐个将堆根交换出去
- sink(a, 1, n-i);
- }
- }
- void sink_nonrecursion(int a[], int i, int n)
- {
- int lc, rc, mc;
- lc = 2*i;
- while(lc <= n) //从i处向下直到叶子结点
- {
- rc = lc + 1;
- mc = (rc > n)?lc : ((a[lc]>a[rc])?lc:rc);
- //mc为较大的孩子
- if(a[i] < a[mc])
- {
- swap(&a[i], &a[mc]);
- //较大的孩子对应的子树发生变化
- i = mc;
- lc = 2*i;
- }
- else
- return;//此时子树不会有变化
- }
- }
- void heap_sort_nonrecursion(int a[], int n)
- {
- int i;
- for(i=n/2;i>=1;i--)
- sink_nonrecursion(a, i, n);
- for(i=1;i<=n;i++)
- {
- swap(&a[1],&a[n-i+1]);
- sink_nonrecursion(a, 1, n-i);
- }
- }
编码时注意:
1、待排序列(同时也是堆)在数组中的起始编号是1还是0?
这关系到求孩子位置的计算公式。 本文中使用的是数组起始编号(即堆的根节点编号)从1开始。也就是说0位置没有用到。
2、区分整个序列的大小和堆的大小。
堆随着轮次的进行越来越小。
辅助代码:
- void swap(int *a, int *b)
- {
- int tmp;
- tmp = *a;
- *a = *b;
- *b = tmp;
- }
- void show(int a[], int n)
- {
- for(int i=1;i<=n;i++)
- printf("%d ",a[i]);
- printf("\n");
- }
- int main()
- {
- int a[] = {0, 1, 3 ,45,5 ,7,66,50 ,9, 2, 4, 6, 8};
- int n = sizeof(a)/sizeof(int)-1;
- show(a, n);
- //heap_sort_nonrecursion(a, n);
- heap_sort_recursion(a, n);
- show(a, n);
- return 1;
- }