堆排序(C语言实现)

一、堆的概念

         所谓堆,它是一个数组,也能够被看成一个近似的全然二叉树。树上每一个结点相应数组的一个元素。二叉堆分为二种:最大堆和最小堆。本文主要介绍最大堆,最小堆类似。最大堆的特点:对于随意某个结点,该结点的值大于左孩子、右孩子的值,可是左右孩子的值没有要求。

二、堆排序算法

     首先,按堆的定义将数组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/2) {
        tmp = h[par]; //记录父母结点键值
        lc = par<<1;
        rc = lc+1;
        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/2; i != 0; i--) {
        heapAdjust(n, i);
    }
}

void
heapSort(int n)
{
    int ntimes = n;

    while (ntimes--) {
        printf("%d\n", h[1]);
        h[1] = h[n];
        h[n--] = 0; //堆清零
        heapAdjust(n, 1);
    }
}

int
main(void)
{    
    int n, i;
    
    scanf("%d", &n);
    h[0] = INF;
    for (i = 1; i != n+1; i++) {
        scanf("%d", &h[i]);
    }
    createHeap(n);
    heapSort(n);
    return 0;
}

/*
  参考测试数据
6
342 31 52 626 12 124
10
43 525 14 21 52 3 52 45 319 15155
*/

 

posted @ 2017-12-02 13:57  SeedQi  阅读(6843)  评论(0编辑  收藏  举报