简单心理 2017-12-02
一、堆的概念
所谓堆,它是一个数组,也能够被看成一个近似的全然二叉树。树上每一个结点相应数组的一个元素。二叉堆分为二种:最大堆和最小堆。本文主要介绍最大堆,最小堆类似。最大堆的特点:对于随意某个结点,该结点的值大于左孩子、右孩子的值,可是左右孩子的值没有要求。
二、堆排序算法
首先,按堆的定义将数组R[0..n]调整为堆(这个过程称为创建初始堆),交换R[0]和R[n];
然后,将R[0..n-1]调整为堆,交换R[0]和R[n-1];
如此反复,直到交换了R[0]和R[1]为止。
以上思想可归纳为两个操作:
(1)根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都比它的孩子结点数值大)。
(2)每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大根堆。
当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。
具体图片我就不找了,网上很多。
三、算法比较
堆排序算法的时间复杂度是O(nlgn),比插入排序要好,跟归并排序相同,但是与归并排序不一样的地方在于,堆排序不需要额外的存储空间,或者说,只需要常数个额外的存储空间,属于内排序算法。
#include <stdio.h> #define N 1000 #define INF 999999999 int h[N]; //调整堆(迭代法) //n:规模 i:二叉子堆的堆顶 void heapAdjust(int n, int par) { int tmp, pos, lc, rc; while (par <= n/) { tmp = h[par]; //记录父母结点键值 lc = par<<; rc = lc+; pos = par; //父母结点至多更新2次 if (h[par] < h[lc]) { h[par] = h[lc]; pos = lc; } if (rc <= n && h[par] < h[rc]) { h[par] = h[rc]; pos = rc; } if (pos == par) //无更新即无需调整 break; else h[pos] = tmp; par = pos; //假设这个位置的结点是“父母结点” } } //创建堆 //规模为n的堆,对其父母结点,自底向上自右向左地调整堆 void createHeap(int n) { int i; for (i = n/; i != ; i--) { heapAdjust(n, i); } } void heapSort(int n) { int ntimes = n; while (ntimes--) { printf("%d\n", h[]); h[] = h[n]; h[n--] = ; //堆清零 heapAdjust(n, ); } } int main(void) { int n, i; scanf("%d", &n); h[] = INF; for (i = ; i != n+; i++) { scanf("%d", &h[i]); } createHeap(n); heapSort(n); return ; } /* 参考测试数据 6 342 31 52 626 12 124 10 43 525 14 21 52 3 52 45 319 15155 */