堆排序

堆排时间复杂度为O(n log n),比快排稳定

这次先贴代码emmm

下次有空再来补解析

自我感觉注释很详细了

 1 #include <cstdio>
 2 int n,m,a[5000005];    //建立大根堆,从小到大排序 
 3 void add(int x,int id)    //往堆中添加节点 
 4 {
 5     int fa;  //  父节点 
 6     a[id]=x;    //把新节点放在堆的最后 
 7     //整理堆,使堆始终保持大根堆的性质 
 8     while (id>1 && a[id/2]<a[id])    // 如果该节点不是堆的根,并且大于它的父亲节点,那么不符合大根堆的性质 
 9     {                             //调整 
10         fa=id>>1;
11         a[fa]^=a[id];  a[id]^=a[fa];  a[fa]^=a[id];    //和父亲节点交换 
12         id=fa;                                     //节点上移,继续调整 
13     } 
14 //因为只有当儿子节点大于父亲节点时才交换,所以最后新节点一定大于它的两个儿子节点
15 //当新节点小于父亲节点时停止交换
16 //这样就保持了大根堆的性质 
17     return;
18 }
19 void heapsort()        //开始堆排 
20 {
21 //每次把根和堆的最后一个节点交换
22 //就是把根取出,暂存在最后,然后整理堆中剩下的节点,找出剩余节点中最大的 
23 //因为是大根堆,每次取出的根节点都是剩余节点中最大的
24 //所以下一次取出的节点肯定比上一次小 
25 //每次都把取出的根节点放在当前堆的最后
26 //所以最后数组从后往前是递减的
27 //所以大根堆排序是从小到大的 
28     while (m>1)    //当堆中只剩下一个节点时,不用再整理了 
29     {
30         int ls,rs,son,i=1;
31         //  ls  左儿子下标  
32         //  rs  右儿子下标
33         //  son  要和当前节点交换的儿子下标  
34         while (i*2<=m)  //儿子还在当前堆的范围内 
35         {
36             ls=i<<1;  rs=ls|1;  
37             son=(rs<=m && a[rs]>a[ls])?rs:ls;  //  找出两个儿子中较大的 
38             if (a[i]>=a[son]) //说明当前节点大于它的两个儿子节点 
39                break;    //结束 
40             a[i]^=a[son];  a[son]^=a[i];  a[i]^=a[son];  //  交换后父节点不小于两个子节点 
41             i=son;      //如果没有结束,说明还需要交换,继续 
42         }
43         a[m]^=a[1];  a[1]^=a[m];  a[m]^=a[1];  m--;
44         //  把根结点放在当前堆的最后 ,减小堆的范围 
45     }
46     return;
47 }
48 int main()
49 {
50     int i,j,x;
51     scanf("%d",&n);
52     scanf("%d",&a[1]);  
53     for (i=2;i<=n;i++)
54     {
55         scanf("%d",&x);
56         add(x,i);
57     } 
58     //添加完节点的堆是一个大根堆 
59     a[n]^=a[1];  a[1]^=a[n];  a[n]^=a[1];  m=n-1;
60     //  把根结点放在当前堆的最后 ,减小堆的范围
61     heapsort();
62     for (i=1;i<=n;i++)
63        printf("%d ",a[i]);
64     return 0;  
65 }

测试了一下发现堆排的速度果然还是比不上C++的sort

原文地址:https://www.cnblogs.com/rabbit1103/p/14033594.html