堆排序是选择排序中一种很重要的一种排序方法
选择排序是在未排序的元素中筛选出最大关键字元素放入已排序序列末尾,选择其实就是查询,最直接无脑的查询方式就是遍历全部未排序的元素查找出最大关键字,查询一次的时间复杂度也就为O(n),而借助一些数据结构则可以在O(logn)的时间内查询出最大关键字,比如二叉搜索树,红黑树,还有就是最高效的也就是实际选用的堆,这样排序的时间复杂度也就降低到了O(nlogn)。
堆结构的特点是对于任意一个节点,他一定是整棵树中以它为根节点的子树的全部节点中关键字最大的一个节点,则排序中需要查找的最大关键字元素就是堆顶也就是整棵树的根节点,不断提取出堆顶元素放入已排序序列然后再维护堆就能完成排序。
首先是建堆,堆是完全二叉树,其实就是优先队列,可以直接调用C++的STL模板中的priority_queue也就是优先队列类通过push,top,pop就能完成排序
也就是
- priority_queue<int,vector<int>,greater<int>> heap;
- for(int i=0;i<1000;i++)heap.push(p[i]); //p是待排序数组
- for(int i=0;i<1000;i++){
- p[i]=heap.top();
- heap.pop();
- }
这样虽然简单但这就不是原址排序了,如果只追求简单快速为什么不直接用库函数sort呢是不是。
基于原址的建堆方法:
因为堆是完全二叉树,完全二叉树能直接用数组存放,可以通过交换数组元素位置建堆,相信大家知道完全二叉树中除根节点外的任意一个编号为n的节点的父节点编号是n/2向下取整。和优先队列插入一个元素相似,可以每次将一个元素通过递归上浮到一个合适的位置。
- void up(int at){
- if(at<=1)return;
- if(all[at]>all[at/2]){
- any temp=all[at];
- all[at]=all[at/2];
- all[at/2]=temp;
- up(at/2);
- }
- }
这种方式必须自顶向下建堆,也就是
- for(int i=1;i<=N;i++)up(i);
这样才能保证每次是插入到一个堆中,保证循环完后能成功建成堆。
第二种建堆方式,通过下沉自底向上的建堆,方法和上浮类似但稍微复杂一点,和两个孩子节点中较大的一个比较,如果小于更大的孩子节点就和这个最大的孩子节点交换位置,递归进行这一操作,直到不存在大于自身的孩子节点时终止递归。
- void down(int at,int end){ //end是堆中目前元素个数
- int maxn=at,max=all[at];
- if(all[at*2]>max&&at*2<=end){
- maxn=at*2;
- max=all[maxn];
- }
- if(all[at*2+1]>max&&at*2+1<=end)maxn=at*2+1;
- if(maxn!=at){
- int temp=all[at];
- all[at]=all[maxn];
- all[maxn]=temp;
- down(maxn,end);
- }
- }
因为最后一个父节点的编号为N/2(N为节点总数量),下沉是父节点和孩子节点比较,叶子节点没有孩子节点可以直接跳过,也就是从N/2开始
- for(int i=N/2;i>1;i--)down(i,N);
如果是要从小到大排序建大顶堆,如果是要从大到小排序则建小顶堆,堆是完全二叉树,每次提取堆顶元素是将堆的最后一个元素和堆顶元素交换(实际也就是空出堆的最后一个位置后放入栈顶元素,而这个位置刚好也就是栈顶元素的正确位置),然后将这唯一一个不符合堆结构性质的暂放在堆顶位置的元素下沉到合适位置。
原本存放堆的那段数组的末尾空出的一个位置作为有序序列的新增部分(这也是递增排序要用大顶堆的原因),将堆顶元素放入这个位置,当从堆中提取完全部元素时有序序列将包含全部元素也就完成了排序。
- #include<iostream>
- #include<cstdlib>
- #include<ctime>
- using namespace std;
- #define MaxN 12000+50
- typedef int any; //对任意数据类型排序,结构体则通过重载运算符
- any all[MaxN],N;
- void up(int at){
- if(at<=1)return;
- if(all[at]>all[at/2]){
- any temp=all[at];
- all[at]=all[at/2];
- all[at/2]=temp;
- up(at/2);
- }
- }
- void down(int at,int end){
- int maxn=at,max=all[at];
- if(all[at*2]>max&&at*2<=end){
- maxn=at*2;
- max=all[maxn];
- }
- if(all[at*2+1]>max&&at*2+1<=end)maxn=at*2+1;
- if(maxn!=at){
- int temp=all[at];
- all[at]=all[maxn];
- all[maxn]=temp;
- down(maxn,end);
- }
- }
- void heapsort(){
- for(int i=1;i<=N;i++)up(i);
- int temp,end=N;
- for(int i=N;i>1;i--){
- temp=all[i];
- all[i]=all[1];
- all[1]=temp;
- down(1,--end);
- }
- }
- int main(){
- srand(time(time_t()));
- for(int i=1;i<=10000;i++)all[i]=rand();
- N=10000;
- heapsort();
- for(int i=1;i<=10000;i++)cout<<all[i]<<endl;
- }