这次介绍堆排序,堆排序分为2步,1.建堆 2.排序 但是建堆的过程是对堆进行调整,而排序的过程实际上也是对堆调整,堆排序,是基于完全二叉树的,凡是和树和图相关的,总是需要多花点时间弄懂,哎,基础太差。
所以关键是对堆的调整,下面的代码和图用的是大顶堆,代码是参考大话数据结构,理解了之后自己动手敲的。
调整的过程如下图所示,但是为了显示一个过程,图7、图8最后的虚线是visio一页不足所致,红色为每次要调整的非叶子结点
排序的过程就是,每次拆掉最后一个结点,然后重新调整堆,见下图
另外需要注意的地方都在代码注释中
void heapAdjust(int a[],int n,int s){
int tmp=a[s],i;
for(i=2*s;i<=n;i*=2){ //1.i=2*s,是因为当前节点是s,那么左孩子一定是2*s 2.为什么是i*=2,也是完全二叉树的性质,每次都是往下比较孩子结点的值
if(i=a[i]) break;
a[s]=a[i]; //调整比父节点大的值,赋值的过程如上图
s=i;
}
a[s]=tmp; //最后调整原来的值应该所在的位置
}
void heapSort(int a[],int n){
int i;
for(i=n/2;i>=1;i--){ //建堆
heapAdjust(a,n,i);
}
for(i=n;i>1;i--){ //排序
swap(&a[1],&a[i]);
heapAdjust(a,i-1,1);
}
}
堆排序总结:
1.堆排序,下标是从1开始的,以前的六种排序可以从0开始也可以从1开始,因为按照完全二叉树的性质,如果1是父节点,那么2和3是他的子节点,如果从0开始的话,那么就是1和2是他的父节点,不满足2*i和2*i+1,反映在代码上就是heapAdjust函数中的for循环的第一个if语句中,i会变成0或者1,而不是预想的1和2,所以堆排序全部是从1到n,a[0]舍弃不用
2.建堆的过程中应该注意,如果有n个数,只需要<=n/2遍即可,因为<=n/2的节点都是非叶子结点,比如完全二叉树,当n=7,那么这个树的叶子结点是4,5,6,7,非叶子结点也就是1,2,3,把1,2,3的节点调整完毕,则整棵树也就满足大顶堆的定义:每个结点的值都大于等于其左右孩子叶子结点的值,反映在代码上就是heapSort函数中的第一个循环
初值是n/2,循环条件是i>=1
3.写代码过程中出错的地方,感觉堆排序过程中,一定要注意各个for循环和if判断中对于边界的判断,for循环还需注意初始值
heapAdjust函数中的第一for循环中是i<=n,而第一个else循环中是i
heapSort函数中的第二个for循环,最后一个数是a[n],故i=n,只剩一个数时无需调整,所以i>1,始终和第一个结点交换,然后堆调整的heapAdjust(a,i-1,1),第一个参数不解释,第三个参数因为每次都和第一个结点交换,所以每次都调整从1开始调整堆,第二个参数原来是i=n个,交换了一个,得到了一个最大值,然后下一次调整的时候边界就是i-1了。
总的代码如下:
#include
#include
#include
#define MAXN 15
void display(int a[],int n){
int i;
for(i=1;i<=n;i++){
printf("%d ",a[i]);
}
printf("\n");
}
void swap(int *s,int *t){
int temp=*s;
*s=*t;
*t=temp;
}
void heapAdjust(int a[],int n,int s){
int tmp=a[s],i;
for(i=2*s;i<=n;i*=2){
if(i=a[i]) break;
a[s]=a[i];
s=i;
}
a[s]=tmp;
}
void heapSort(int a[],int n){
int i;
for(i=n/2;i>=1;i--){
heapAdjust(a,n,i);
}
for(i=n;i>1;i--){
swap(&a[1],&a[i]);
heapAdjust(a,i-1,1);
}
}
int main(int argc, char *argv[]) {
unsigned int seed;
seed = time(0);
srand(seed);
int i,a[MAXN];
for(i=1;i
未完待续,还有排序算法的总结
如需参考排序算法总结:排序算法四总结
如果文章有什么错误或者有什么建议,欢迎提出,大家共同交流,一起进步
文章转载请注明出处,请尊重知识产权