堆排序总结
堆排序思想:最大堆的堆顶元素是全部数据中的最大值,输出这个值
。再将剩余元素整理成新的最大堆,此时堆顶元素有时又是剩余元素
的最大值,再输出这个值。继续这个过程,每次输出堆顶元素,并将
剩余元素整理成新的最大堆再输出...
堆排序要解决的几个问题
1:如何将数据排列成堆的形式——初始堆的建立
2:输出堆顶元素后,剩余元素如何再整理成新的堆——堆的整理
3:输出元素放在什么位置
预备知识:
1: 堆中的元素存储在一个数组中,根据堆中的各元素之间具有
有序性关系,可以使用二叉树的方式来表示一个堆。因为各元素从前
至后存放在数组的钱n 个单元中,所以所画的二叉树实际上是一颗完全
二叉树
2: 所以根据完全二叉树的性质,数组前一半的数据都是分值节点,后一半
的数据都是叶子节点
3: 即如果有七个数,分别占据数组的a[1]-a[7],那么7/2=3,所以a[1],
a[2],a[3]为分支节点,剩下的为叶子节点
4: a[i]的左儿子为a[i*2],右儿子为a[i*2+1],如a[1]的左右儿子分别为
a[2]与a[3]。(这有赖于数组从a[0]开始,还是从a[1]开始)
建堆的过程:
见代码,见实例图
/*********************************************************/ /*数组从下标1开始*/ #include<iostream> #define L(t) ((t)<<1) #define R(t) ((t)<<1 | 1) usingnamespace std; intconst NUMBER = 1000; int trees[NUMBER]; int N; /*********************************************************/ void swap(int &a,int &b){ int temp = a; a = b; b = temp; } /*********************************************************/ //a[i]元素向下沉,此时堆中的实际元素为n,被甩在后面的元素 //已经不用管了 void ShiftDown(int i,int n){ int child; //i<=n/2说明trees[i]为分支结点 //i=child;该句在循环最后执行,即从上往下递归判断 //新的位置是否已经稳定,不稳定继续下沉 for(;i<=n/2;i=child){ child = i*2;//先试着将预交换的元素设为左儿子 if((child != n) && trees[child+1]>trees[child]) child=child+1;//如果右儿子比左儿子大 //则将预交换儿子设置为右儿子 if(trees[child]>trees[i])//如果该节点不如较大的 //儿子大,则下沉(及交换) swap(trees[child],trees[i]); } } /*********************************************************/ void HeapSort(){ //该循环极为初始堆的建立,从最后一个分支结点开始 for(int i=N/2;i>=1;i--){ ShiftDown(i,N); } //每次循环首先使堆顶元素和首元素交换,再将首元素 //下沉到合适的位置,记得每次实际堆中元素减1 for(int i=N;i>=1;i--){ swap(trees[1],trees[i]);//最大的放到末尾 ShiftDown(1,i-1); //所以此时的实际数量为i-1 } } /*********************************************************/ int main(){ while(cin>>N&&N!=0){ for(int i=1;i<=N;i++) cin>>trees[i]; HeapSort(); for(int i=1;i<=N;i++) cout<<trees[i]<<" "; cout<<endl; } return0; }