堆排序(算法导论)
堆排序:最大堆最小堆以及依赖于最大堆最小堆的最大优先级队列和最小优先级队列。
堆排序不是一种稳定的排序方法,时间复杂度为O(1),空间复杂度为O(n*logn)
以下列出我的创建的方法的声明:
//begin
/*保持大根堆非递归版本O(lgn)*/
void max_heapify(int *a,int i,int num);
/*保持大根堆递归版本O(lgn)*/
void max_heapify1(int *a,int i,int num);
/*创建一个大根堆O(n)*/
void build_max_heapify(int *a,int num);
/*大根堆排序O(nlgn)*/
void max_heap_sort(int *a,int num);
//end
//begin
/*保持小根堆非递归版本O(lgn)*/
void min_heapify(int *a,int i,int num);
/*保持小根堆递归版本O(lgn)*/
void min_heapify1(int *a,int i,int num);
/*创建一个小根堆O(n)*/
void build_min_heapify(int *a,int num);
/*小根堆排序O(nlgn)*/
void min_heap_sort(int *a,int num);
//end
//begin
/*最大优先级队列插入*/
void insertmax(int *a,int x,int num);
/*优先级队列返回最大值*/
int maxinum(int *a);
/*弹出第一个元素*/
int extractmax(int *a,int num);
/*把某个元素的值增加到指定值*/
void increaseMaxKey(int *a,int x,int key);
//end
//begin
/*最小优先级队列插入*/
void insertmin(int *a,int x,int num);
/*优先级队列返回最小值*/
int mininum(int *a);
/*弹出第一个元素*/
int extractmin(int *a,int num);
/*把某个元素的值减小到指定值*/
void increaseMinKey(int *a,int x,int key);
//end
/*打印数组*/
void mprint(int *a,int n);
#include<stdio.h> #include<string.h> #include<stdlib.h> //凡是大小根堆都是从a[1]开始保存数据的,因为方便 //关键是保持最大堆的性质 //begin /*保持大根堆非递归版本O(lgn)*/ void max_heapify(int *a,int i,int num); /*保持大根堆递归版本O(lgn)*/ void max_heapify1(int *a,int i,int num); /*创建一个大根堆O(n)*/ void build_max_heapify(int *a,int num); /*大根堆排序O(nlgn)*/ void max_heap_sort(int *a,int num); //end //begin /*保持小根堆非递归版本O(lgn)*/ void min_heapify(int *a,int i,int num); /*保持小根堆递归版本O(lgn)*/ void min_heapify1(int *a,int i,int num); /*创建一个小根堆O(n)*/ void build_min_heapify(int *a,int num); /*小根堆排序O(nlgn)*/ void min_heap_sort(int *a,int num); //end //begin /*最大优先级队列插入*/ void insertmax(int *a,int x,int num); /*优先级队列返回最大值*/ int maxinum(int *a); /*弹出第一个元素*/ int extractmax(int *a,int num); /*把某个元素的值增加到指定值*/ void increaseMaxKey(int *a,int x,int key); //end //begin /*最小优先级队列插入*/ void insertmin(int *a,int x,int num); /*优先级队列返回最小值*/ int mininum(int *a); /*弹出第一个元素*/ int extractmin(int *a,int num); /*把某个元素的值减小到指定值*/ void increaseMinKey(int *a,int x,int key); //end /*打印数组*/ void mprint(int *a,int n); int main(){ freopen("F://学习//算法//codeblock//11//in.txt","r",stdin); int n,a[100]; scanf("%d",&n); int i; for(i=1;i<=n;i++){ scanf("%d",&a[i]); } printf("打印原始数组数据:\n"); mprint(a,n); build_max_heapify(a,n); printf("打印创建的大根堆数组数据:\n"); mprint(a,n); max_heap_sort(a,n); printf("打印大根堆排序后的数组数据:\n"); mprint(a,n); printf("====================================\n"); printf("打印打印插入一个元素后大根堆的数组数据:\n"); insertmax(a,100,n); n=n+1; mprint(a,n); printf("打印大根堆的最大的数据:\n"); printf("%d\n",maxinum(a)); printf("打印大根堆弹出最大元素后的数组数据:\n"); int x=extractmax(a,n); printf("%d\n",x); n=n-1; mprint(a,n); printf("打印大根堆第五个元素增大到101后的数组数据:\n"); increaseMaxKey(a,5,101); mprint(a,n); printf("====================================\n"); build_min_heapify(a,n); printf("打印创建的小根堆数组数据:\n"); mprint(a,n); min_heap_sort(a,n); printf("打印小根堆排序后的数组数据:\n"); mprint(a,n); printf("====================================\n"); printf("打印小根堆第4个元素减小到7后的数组数据:\n"); build_min_heapify(a,n);//先使数组变成最小堆 mprint(a,n);//打印数组变成最小堆之后的数据 increaseMinKey(a,4,7); mprint(a,n); insertmin(a,2,n); printf("打印小根堆插入一个元素后的数组数据:\n"); n+=1; mprint(a,n); printf("打印小根堆最小的元素数据:\n"); printf("%d\n",mininum(a)); printf("打印弹出小根堆最小的元素后的数组数据:\n"); int min=extractmin(a,n); printf("%d\n",min); mprint(a,n); //freopen("F://学习//算法//codeblock//11//out.txt","w",stdout); //fclose(stdout); return 0; } /*保持大根堆非递归版本*/ void max_heapify(int *a,int i,int num){ int max=0; while(i<=num/2){ int l=2*i,r=2*i+1; if(l<=num&&a[l]>a[i]){ max=l; }else{ max=i; } if(r<=num&&a[r]>a[max]){ max=r; } if(i!=max){ int temp=a[i]; a[i]=a[max]; a[max]=temp; i=max; }else{ break; } } } /*保持大根堆递归版本*/ void max_heapify1(int *a,int i,int num){ int l=2*i,r=2*i+1; int max=0; if(l<=num&&a[l]>a[i]){ max=l; }else{ max=i; } if(r<=num&&a[r]>a[max]){ max=r; } if(i!=max){ int temp=a[i]; a[i]=a[max]; a[max]=temp; max_heapify1(a,max,num); } } /*创建一个大根堆*/ void build_max_heapify(int *a,int num){ int i; for(i=num/2;i>=1;i--){ max_heapify(a,i,num); } } /*大根堆排序*/ void max_heap_sort(int *a,int num){ int i; for(i=num;i>=2;){ int temp=a[i]; a[i]=a[1]; a[1]=temp; max_heapify(a,1,--i); } } /*保持小根堆非递归版本*/ void min_heapify(int *a,int i,int num){ int min=0; while(i<=num/2){ int l=2*i,r=2*i+1; if(l<=num&&a[l]<a[i]){ min=l; }else{ min=i; } if(r<=num&&a[r]<a[min]){ min=r; } if(i!=min){ int temp=a[i]; a[i]=a[min]; a[min]=temp; i=min; }else{ break; } } } /*保持小根堆递归版本*/ void min_heapify1(int *a,int i,int num){ int l=2*i,r=2*i+1,min=0; if(l<=num&&a[l]<a[i]){ min=l; }else{ min=i; } if(r<=num&&a[r]<a[min]){ min=r; } if(min!=i){ int temp=a[i]; a[i]=a[min]; a[min]=temp; min_heapify1(a,min,num); } } /*创建一个小根堆*/ void build_min_heapify(int *a,int num){ int i; for(i=num/2;i>=1;i--){ min_heapify(a,i,num); } } /*小根堆排序*/ void min_heap_sort(int *a,int num){ int i; for(i=num;i>=2;){ int temp=a[i]; a[i]=a[1]; a[1]=temp; min_heapify(a,1,--i); } } /*优先级队列插入*/ void insertmax(int *a,int x,int num){ num+=1; a[num]=-0x7fffffff; increaseMaxKey(a,num,x); } /*优先级队列返回最大值*/ int maxinum(int *a){ return a[1]; } /*弹出第一个元素*/ int extractmax(int *a,int num){ int max=a[1]; a[1]=a[num]; num-=1; max_heapify(a,1,num); return max; } /*把某个元素的值增加到指定值*/ void increaseMaxKey(int *a,int i,int key){ if(key<=a[i]){ printf("error\n"); } a[i]=key; while(i/2>=1&&a[i/2]<a[i]){ int t=a[i/2]; a[i/2]=a[i]; a[i]=t; i=i/2; } } /*最小优先级队列插入*/ void insertmin(int *a,int x,int num){ num+=1; a[num]=0x7fffffff; increaseMinKey(a,num,x); } /*优先级队列返回最小值*/ int mininum(int *a){ return a[1]; } /*弹出第一个元素*/ int extractmin(int *a,int num){ int min=a[1]; a[1]=a[num]; num-=1; min_heapify(a,1,num); return min; } /*把某个元素的值减小到指定值*/ void increaseMinKey(int *a,int x,int key){ if(key>=a[x]){ printf("error\n"); } a[x]=key; while(x/2>=1&&a[x/2]>a[x]){ int t=a[x/2]; a[x/2]=a[x]; a[x]=t; x=x/2; } } /*打印数组*/ void mprint(int *a,int n){ int i; for(i=1;i<=n;i++){ printf("%d ",a[i]); } printf("\n"); }
以最大堆为例,关键方法是保持最大堆的性质(方法 max_heapify1()),即父亲节点的值必须比子节点要大。假设父节点i,则他的子节点为2*i和2*i+1,找出这三个节点中值最大的,父节点与这个最大的节点交换,如果最大的节点就是父节点或者这个节点的子节点都不是父节点,那么方法调用结束,否则递归调用该方法。
创建一个大根堆,只需要从第i=num/2开始的节点调用 max_heapify1(),并且把该节点的父节点赋给i,并且继续调用 max_heapify1(),直到i=1,结束该方法的调用。至于为什么是从第num/2个节点开始的,是因为二叉树的性质,总共有num个节点,那么最后一个父节点就是num/2。
最大堆排序,只需要把最后一个节点和第一个节点交换,然后对第一个节点调用一次 max_heapify1()来保持最大堆的性质,最后的输出顺序是从小到大输出。
最大堆最小堆类似,最大优先级队列以及最小优先级队列都是基于最大堆和最小堆来实现的,理解了最大堆,可以很轻松的理解其他三个。
My cnblogs is :
http://www.cnblogs.com/God-froest/
Welcome you to technology exchange with me.
《死の舞踏》
---Maksim Mrvica
Piano music can develop the temperament of a man.
爱情终将消失于茫茫的时间洪流之中,沉淀于厚重的黄泥沙丘之下...