堆排序
堆排序用到的是完全二叉树的性质,所以在看这篇文章之前先学习完全二叉树。
http://baike.baidu.com/link?url=HUZLyRNtNhAUzf7rgffW3IHkPbNQF_0vO63EvTKZBgYztrMz02B2_-PlCV5eFg6J8y8w8sHEu4Bd7bZVOXozaK
那么,学会了完全二叉树,现在就可以学习堆排序了。
个人认为,堆排序是插入排序和快排思想的联合,所以读者可以先学会插入排序和快排。
插入排序:每次选出带排序序列的最大值,加入已排好序的序列;
快排:在待排序序列中选择一个记录,让它左边的都比它小,右边的都比它大,如此递归。
思考:
对于插入排序,时间复杂度主要在选择最大值的过程中。有没有一种方法可以用更少的时间就选出最大值呢?
对于快速排序,如果用完全二叉树存储,让根节点比左子树大,比右子树也大,那这个根节点不就是最大值吗?
两相结合,堆排序应运而生,而核心就是完全二叉树的性质。下面可以看看堆排序相关的概念,有助于理解。
http://baike.baidu.com/link?url=Fc3__n67naxdKu_l7MzWi6WCn4QbbJESFSKiJUvvO-CzszGyoqv0coneADUg6DjcJEAVpymvhqwm8qRe9gEYSK
下面是堆排序的代码:
1 #include<stdio.h> 2 void creatheap(int *a,int n,int i) //整理堆的函数,包括三个参数,数组首地址,数组元素个数,要处理的结点编号 3 { 4 if (i*2+1>n) //如果 这个结点没有右孩子 5 { 6 if (i*2>n) return; //如果再没有左孩子,直接return 7 else //如果有左孩子,就比较这个结点和左孩子是否需要交换 8 { 9 if (a[i*2]>a[i]) 10 { 11 a[0]=a[i]; 12 a[i]=a[i*2]; 13 a[i*2]=a[0]; 14 creatheap(a,n,2*i); //递归左孩子 15 } 16 else return; //不需要交换的话直接return 17 } 18 } 19 else //如果有右孩子(那么肯定有左孩子啦~) 20 { 21 if (a[i*2+1]<=a[i]&&a[i*2]<=a[i]) return; //经过比较左右孩子都不需要交换,说明这个堆不用处理了,return 22 else{ //否则,这个堆需要处理 23 int maxi=i*2; //下面分析需要处理左孩子还是右孩子 24 if (a[i*2+1]>a[i*2]) maxi++; 25 a[0]=a[i]; 26 a[i]=a[maxi]; 27 a[maxi]=a[0]; 28 creatheap(a,n,maxi); //递归需要处理的那个 29 } 30 } //这样就完成了一次整理 31 } 32 int main() 33 { 34 int b[101]={0}; 35 int n; 36 scanf("%d",&n); 37 int i; 38 for (i=1;i<=n;i++) 39 { 40 scanf("%d",b+i); 41 } 42 for (i=n/2;i>=1;i--) //对非叶子结点递减整理,n/2是第一个非叶子结点的编号 43 { 44 creatheap(b,n,i); 45 } 46 //这样整理完之后目前这个数组是一个堆了 可以确定的是第一个元素b[1]一定是最值 47 int k=n; 48 while (n!=1) 49 { 50 b[0]=b[1]; 51 b[1]=b[n]; 52 b[n]=b[0]; 53 n--; 54 creatheap(b,n,1); //把b[1]移到末尾,然后同理整理前n-1个元素,直到n=1 55 } 56 for (i=1;i<=k;i++) //堆排序完成 57 { 58 printf("%d ",b[i]); 59 } 60 return 0; 61 }