分享

堆排序算法

 启蒙彩魂 2010-12-12
堆的定义:    
       n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):
   (1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤)

   若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

       堆的这个性质使得可以迅速定位在一个序列之中的最小(大)的元素.

       堆排序算法的过程如下:1)得到当前序列的最小(大)的元素 2)把这个元素和最后一个元素进行交换,这样当前的最小(大)的元素就放在了序列的最后,而原先的最后一个元素放到了序列的最前面 3)的交换可能会破坏堆序列的性质(注意此时的序列是除去已经放在最后面的元素),因此需要对序列进行调整,使之满足于上面堆的性质.重复上面的过程,直到序列调整完毕为止.
  1. // array是待调整的堆数组,i是待调整的数组元素的位置,length是数组的长度
  2. void HeapAdjust(int array[], int i, int nLength)
  3. {
  4.   int nChild, nTemp;

  5.   for (nTemp = array[i]; 2 * i + 1 < nLength; i = nChild)
  6.   {
  7.       // 子结点的位置是 父结点位置 * 2 + 1
  8.       nChild = 2 * i + 1;

  9.       // 得到子结点中较大的结点
  10.       if (nChild != nLength - 1 && array[nChild + 1] > array[nChild])
  11.           ++nChild;

  12.       // 如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
  13.       if (nTemp < array[nChild])
  14.       {
  15.           array[i] = array[nChild];
  16.       }
  17.       else    // 否则退出循环
  18.       {
  19.           break;
  20.       }
  21.   }

  22.   // 最后把需要调整的元素值放到合适的位置
  23.   array[i] = nTemp;
  24. }

  25. // 堆排序算法
  26. void HeapSort(int array[], int length)
  27. {
  28.   // 调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
  29.   for (int i = length / 2 - 1; i >= 0; --i)
  30.   {
  31.       HeapAdjust(array, i, length);
  32.   }

  33.   // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
  34.   for (int i = length - 1; i > 0; --i)
  35.   {
  36.       // 把第一个元素和当前的最后一个元素交换,
  37.       // 保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
  38.       Swap(&array[0], &array[i]);

  39.       // 不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
  40.       HeapAdjust(array, 0, i);
  41.   }
  42. }
复制代码
一个测试及输出的结果,在每次HeapAdjust之后显示出来当前数组的情况
before Heap sort:
71 18 151 138 160 63 174 169 79 78
// 开始调整前半段数组元素
71 18 151 138 160 63 174 169 79 78
71 18 151 169 160 63 174 138 79 78
71 18 174 169 160 63 151 138 79 78
71 169 174 138 160 63 151 18 79 78
174 169 151 138 160 63 71 18 79 78
// 开始进行全局的调整
169 160 151 138 78 63 71 18 79 174
160 138 151 79 78 63 71 18 169 174
151 138 71 79 78 63 18 160 169 174
138 79 71 18 78 63 151 160 169 174
79 78 71 18 63 138 151 160 169 174
78 63 71 18 79 138 151 160 169 174
71 63 18 78 79 138 151 160 169 174
63 18 71 78 79 138 151 160 169 174
18 63 71 78 79 138 151 160 169 174
//*****************************************
/*
  Name:       堆排序算法
  Author:       zhuqing
  Des cription:       堆排序算法的过程演示 
  Date: 18-08-03 09:50
  Copyright:
*/
#include<stdio.h>
#define N 6
int k,j;
/* 建堆函数 */
void build(int *a,int i,int n){
    int tmp;
    k=i;
    j=2*k+1;
    while(j<=n){
        if(j<n && a[j]<a[j+1])
            j++;
        if(a[k]>=a[j])break;
        tmp=a[k];
        a[k]=a[j];
        a[j]=tmp;       
        k=j;
        j=2*j+1;
    }
}
/* 打印数组函数 */
void prnt(int *a,int n){
    int i;
    printf("\n");
    for(i=0;i<n;i++){
        printf("%3d",a[i]);
    }
    printf("\n");
}
/* 打印堆函数 */
void prnthp(int *a,int b1,int b2){
    int size;
    int h=0,sum=0,item=1;
    int i,j,cnt,start,tmp;
    size=b2-b1+1;
    while(sum<=size){
        sum+=item;
        h++;
        item*=2;
    }
    item=1;
    cnt=0;
    start=b1;
    tmp=1;
    printf("\n--------------------------------------------\n");   
    printf("  堆:\n");
    while(1){
        for(i=0;i<h;i++){
                for(j=0;j<h-i;j++)
                                printf("    ");
                 for(j=0;j<i+1;j++)printf(" ");
                for(j=0;j<tmp;j++){
                                if(cnt>size-1)goto end;
                                printf("%4d",a[cnt++]);
                }
                printf("\n");
                tmp*=2;
        }      
     }
     end:
          printf("\n");        
          return;                 
}
/* 打印已排序数组函数 */
void prntar(int *a,int b2,int len){
  int i;
  printf("  已排序:\n");
  for(i=0;i<b2;i++){
    printf("   ");
  }         
  for(i=b2;i<=len;i++){
    printf("%3d",a[i]);
  }         
  printf("\n");
}
main(){
    /* int a[]={-1,2,5,1,0,-3,-2,8,6}; */
    int a[50];
    int i;
    int tmp;
    int sum;
    int num;
    int len;
    printf("              堆排序\n");
    printf("\n============================================\n");   
    printf("\n  请输入待排序数组个数,以回车结束:\n");
    scanf("%3d",&len);   
    printf("\n  请输入待排序数组,以回车结束:\n");
    for(i=0;i<len;i++)
        scanf("%3d",&a[i]);       
    tmp=1;sum=0;
    while(sum+tmp<=len){
        sum+=tmp;   
        tmp*=2;
    }
    printf("\n============================================\n");   
    printf("\n  初始数组:            \n");   
    prnt(a,len);   
    /* 建初始堆 */
    for(i=sum-1;i>=0;i--)
        build(a,i,len-1);      
    prnthp(a,0,len-1);     
    /* 改建堆 */
    for(i=0;i<len-1;i++){
        tmp=a[0];
        a[0]=a[len-1-i];
        a[len-1-i]=tmp;
        build(a,0,len-2-i);
        prnthp(a,0,len-2-i);
        prntar(a,len-1-i,len-1);       
    }
    printf("\n--------------------------------------------\n");
    printf("\n  排序结果:\n");       
    prnt(a,len);
    printf("\n============================================\n\n");   
    getch();
}
//*****************************************************************
 

堆排序简介:

堆排序在最坏的情况下,其时间复杂度也能达到O(nlogn)。相对于快速排序来说,这是它最大的优点,此外,堆排序仅需要一个记录大小供交换用的辅助存储空间。

堆排序的数据结构是二叉堆,二叉堆的特点有两个,一个是它是一棵完全二叉树,另一个是它的根结点小于孩子结点,所以我们很容易找到它的最小结点----根结点;当然如果你想找到最大结点的话,那就要扫描所有的叶子结点,这是很费时间的,如果你想找的是最大结点的话,你最好把它弄成一个大顶堆,即一棵根结点大于孩子结点的完全二叉树。

二叉堆通常用数组来实现,它舍弃下标0,从下标1开始置数,则很容易满足,对于数组中任意位置i上的元素,其左儿子的位置在2i上,右儿子的位置在2i+1上,双亲的位置则在i/2上。

 堆排序的算法之一是把数组构建成二叉堆----这只要增添一个长度为n+1的辅助空间,然后把原数组的元素依次插入到二叉堆即可。然后删除二叉堆的根,把它作为排序后的数组的第一个元素,然后使二叉堆的长度减1,并通过上移使得新得到的序列仍为二叉堆,再提取新二叉堆的第一个元素到新数组。依此类推,直到提取最后一个元素,新得到的数组就是排序后的数组。

堆排序在最坏的情况下,其时间复杂度也能达到O(nlogn)。相对于快速排序来说,这是它最大的优点,此外,堆排序仅需要一个记录大小供交换用的辅助存储空间。

堆排序的数据结构是二叉堆,二叉堆的特点有两个,一个是它是一棵完全二叉树,另一个是它的根结点小于孩子结点,所以我们很容易找到它的最小结点----根结点;当然如果你想找到最大结点的话,那就要扫描所有的叶子结点,这是很费时间的,如果你想找的是最大结点的话,你最好把它弄成一个大顶堆,即一棵根结点大于孩子结点的完全二叉树。

二叉堆通常用数组来实现,它舍弃下标0,从下标1开始置数,则很容易满足,对于数组中任意位置i上的元素,其左儿子的位置在2i上,右儿子的位置在2i+1上,双亲的位置则在i/2上。

堆排序的算法之一是把数组构建成二叉堆----这只要增添一个长度为n+1的辅助空间,然后把原数组的元素依次插入到二叉堆即可。然后删除二叉堆的根,把它作为排序后的数组的第一个元素,然后使二叉堆的长度减1,并通过上移使得新得到的序列仍为二叉堆,再提取新二叉堆的第一个元素到新数组。依此类推,直到提取最后一个元素,新得到的数组就是排序后的数组。

根据要求实现程序如下:

#include <stdio.h>

void push_heap(int *array, int pos, int value)

{

    int i;

    for(i=pos; i/2>0 && array[i/2]>value; i/=2) {

        array[i] = array[i/2];

    }

    array[i] = value;

}

void make_heap(int *data, int *array, int len)

{

    for(int i=0; i<len; ++i) {

        push_heap(array, i+1, data[i]);

    }

}

int pop_heap(int *array, int last)

{

    int value = array[1];

    array[1] = array[last];

    int i=1;

    while(2*i < last) {

        int min = array[2*i] < array[2*i+1] ? array[2*i] : array[2*i+1];

        int pos = array[2*i] < array[2*i+1] ? (2*i) : (2*i+1);

        if(array[i] > min) {

            int tmp = array[i];

            array[i] = min;

            array[pos] = tmp;

            i = pos;

        }

    }

    return value;

}

void sort_heap(int *data, int *array, int len)

{

    for(int i=0; i<len; ++i) {

        data[i] = pop_heap(array, len-i);

    }

}

int main()

{

    int data[] = {12, 34, 23, 3, 1, 45, 87, 99};

    int array[10];

    make_heap(data, array, 8);

    for(int i=1; i<=8; ++i) {

        printf("%d ", array[i]);

    }

    printf("\n");

    sort_heap(data, array, 8);

    for(int i=0; i<8; ++i) {

        printf("%d ", data[i]);

    }

    printf("\n");

    return 0;

}

 

堆排序的空间复杂度要求O(1),上面程序使用了辅助数组,所以其空间复杂度为O(n),不符合要求,所以又写了一个,如下:

#include <stdio.h>

int swap(int *a, int *b)

{

        int tmp = *a;

        *a = *b;

        *b = tmp;

}

void adjust(int *array, int i, int n)

{

        int min, pos;

        while(1) {

                if((2*i+2) < n) {

                        min = array[2*i+1] < array[2*i+2] ? array[2*i+1] : array[2*i+2];

                        pos = array[2*i+1] < array[2*i+2] ? (2*i+1) : (2*i+2);

                        if(array[i] < min) { break; }

                        swap(&array[i], &array[pos]);

                        i = pos;

                } else if((2*i+2) == n) {

                        min = array[2*i+1];

                        pos = 2*i+1;

                        if(array[i] < min) { break; }

                        swap(&array[i], &array[pos]);

                        i = pos;

                } else {

                        break;

                }

        }

}

void heap_sort(int *array, int n)

{

//构建最小堆

        for(int j=(n-1)/2; j>=0; --j) {

                adjust(array, j, n);

        }

//降序排列

        for(int i=n; i>1; --i) {

                swap(&array[0], &array[i-1]);

                adjust(array, 0, --n);

        }

}

int main()

{

        int data[] = {12, 34, 23, 3, 1, 45, 87, 99};

        heap_sort(data, 8);

        for(int i=0; i<8; ++i) {

                printf("%d ", data[i]);

        }

        printf("\n");

        return 0;

}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多