【笔记】堆排序

来源:互联网 发布:金庸 知乎 编辑:程序博客网 时间:2024/06/07 19:01

  堆排序只需要记录一个记录大小的辅助空间,每个待排序的记录仅占有一个存储空间。
  **堆的定义:**n个元素的序列{k1,k2,,kn}当且仅当满足如下关系时,称之为堆。

kik2ikik2i+1


kik2ikik2i+1

(i = 1,2,3,…,[n2])

  若将和此序列对应的一维数组看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端节点的值均不大于其左、右孩子结点的值。由此,若序列{k1,k2,,kn}是堆,则堆顶元素必定为序列中n个元素的最小值(或最大值)。

  例如序列{96,83,27,38,11,09}和{12,36,24,85,47,30,53,91}对应的完全二叉树为:


这里写图片描述

  若在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序

  • 调整堆

  如下图所示,假设输出堆顶元素之后,以堆中最后一个元素替代,此时根结点的左、右子树均为堆,则仅需自上至下调整即可。首先以堆顶元素和其左、右子树根结点的值进行比较,由于右子树根结点的值相爱哦与左子树结点的值,则将27和97交换,由于97的替代了27之后破坏了右子树的堆,则需进行和上述相同的调整,直至叶子结点,此时堆顶为n-1个元素中的最小值。重复上述过程,将堆顶元素27和堆中最后一个元素97交换且调整,得到一个新的堆。我们称这个自堆顶至叶子的调整过程为筛选


这里写图片描述

  • 建堆

  从一个无序序列建堆的过程就是一个反复筛选的过程。若将次序列看成是一个完全二叉树,则最后一个非终端结点是第[n2]个元素,由此筛选只需从第[n2]个元素开始。

  例如下图中的二叉树表示一个有8个元素的无序序列{49,38,65,97,76,13,27,49},则筛选从第4个元素开始,由于97>49,则交换之。同理,在第3个元素65被筛选之后序列的状态如图所示。由于第2个元素38不大于其左、右子树根的值,则筛选后的序列不变。


这里写图片描述

  基本算法思想:使记录序列按关键字非递减有序排列,则在堆排序的算法中先建立一个最大堆,即先选得一个关键字为最大的记录并与序列中最后一个记录交换,然后对序列中前n-1记录进行筛选,重新将它调整为一个最大堆,如此分那副直至排序结束。

  • 类型定义
 #include<stdio.h> #define EQ(a,b) ((a)==(b)) #define LT(a,b) ((a)<(b)) #define LQ(a,b) ((a)<=(b)) #define N 8 #define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */ typedef int InfoType; /* 定义其它数据项的类型 */ typedef int KeyType; /* 定义关键字类型为整型 */ typedef struct {   KeyType key; /* 关键字项 */   InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */ }RedType; /* 记录类型 */ typedef struct {   RedType r[MAXSIZE+1]; /* r[0]闲置或用作哨兵单元 */   int length; /* 顺序表长度 */ }SqList; /* 顺序表类型 */ typedef SqList HeapType; /* 堆采用顺序表存储表示 */
  • 堆排序函数
void HeapAdjust(HeapType *H,int s,int m) /* 算法10.10 */ { /* 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,本函数 */   /* 调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆(对其中记录的关键字而言) */   RedType rc;   int j;   rc=(*H).r[s];   for(j=2*s;j<=m;j*=2)   { /* 沿key较大的孩子结点向下筛选 */     if(j<m&&LT((*H).r[j].key,(*H).r[j+1].key))       ++j; /* j为key较大的记录的下标 */     if(!LT(rc.key,(*H).r[j].key))       break; /* rc应插入在位置s上 */     (*H).r[s]=(*H).r[j];     s=j;   }   (*H).r[s]=rc; /* 插入 */ } void HeapSort(HeapType *H) { /* 对顺序表H进行堆排序。算法10.11 */   RedType t;   int i;   for(i=(*H).length/2;i>0;--i) /* 把H.r[1..H.length]建成大顶堆 */     HeapAdjust(H,i,(*H).length);   for(i=(*H).length;i>1;--i)   { /* 将堆顶记录和当前未经排序子序列H.r[1..i]中最后一个记录相互交换 */     t=(*H).r[1];     (*H).r[1]=(*H).r[i];     (*H).r[i]=t;     HeapAdjust(H,1,i-1); /* 将H.r[1..i-1]重新调整为大顶堆 */   } }
  • 主程序
void print(HeapType H) {   int i;   for(i=1;i<=H.length;i++)     printf("(%d,%d)",H.r[i].key,H.r[i].otherinfo);   printf("\n"); } void main() {   RedType d[N]={{49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8}};   HeapType h;   int i;   for(i=0;i<N;i++)     h.r[i+1]=d[i];   h.length=N;   printf("排序前:\n");   print(h);   HeapSort(&h);   printf("排序后:\n");   print(h); }
  • 测试结果


这里写图片描述