堆排序

什么是堆

堆是一种特殊的数据结构,其本质是一棵用数组存储的一棵完全二叉树。

堆的性质

  1. 一个结点,对应数组元素Heap[i],其父结点为Heap[i/2],左子结点Heap[2*i],右子结点Heap[2*i+1]。
  2. 一个堆中,任何一个结点必须大于或小于其子结点。大于的称大根堆,小于的称小根堆。

堆的操作

下列操作均以小根堆为例

插入

将新结点放于队尾,为了使堆的性质2成立,直接模拟即可,时间复杂度O(logn):
1. 如果当前结点大于等于父结点,或者当前节点已经是根结点,结束。
2. 否则将其与父节点交换,转1。

程序实现

 1 void Put(int x)
 2 {
 3     int Son;
 4      Heap[++Len]=x;//放于堆底
 5     Son=Len;
 6     while(Len!=1&Heap[Son/2]>Heap[Son])
 7     {//如果还不是根结点并且堆的性质仍然不满足
 8         Swap(Heap[Son],Heap[Son/2]);//交换
 9         Son/=2;//转移当前结点
10     }
11 }

删除

将堆底的结点来替换根结点,然后向下调整,时间复杂度O(logn):
1. 将堆底的结点替换根结点,标记为当前结点
2. 若当前结点比两个子结点都小,则对的性质满足,退出。若其已经是叶子结点,退出。
3. 否则去两个子结点中最小的与交换,转2。为什么要取最小的与之交换呢?因为如果取的事大的,则作为另一个子结点的父结点时,一定会大于它,不满足对的性质。

程序实现

 1 int Get()
 2 {
 3     int re=Heap[1],Son,Fa;//存下根结点的值,以便返回
 4     Heap[1]=Heap[Len];//将尾结根结点点替换
 5     Len--;//删除了,肯定堆长要少一个嘛
 6     Fa=1;//当前父结点,
 7     while(Fa*2<=Len)//保证它有子结点
 8     {
 9         if(Fa*2+1>Len||Heap[Fa*2]<Heap[Fa*2+1])//选左子结点的条件是比右子结点小,或者当前父节点没有右子结点
10             Son=Fa*2;
11         else Son=Fa*2+1;//若右子结点比左子结点小,选它
12         if(Heap[Fa]>Heap[Son])//如果最小的都没当前父节点小,那就已经是一个堆了
13         {
14             Swap(Heap[Fa],Heap[Son]);//交换
15             Fa=Son;//转移
16         }
17         else //既然已经是一个堆了,直接退出
18             break;
19     }
20     return re;//返回原根结点的值
21 }

建堆

第一种方法:将每一个元素都插入堆,时间复杂度O(nlogn)

核心代码

1 for(int i=1;i<=n;++i)
2 {
3     scanf("%d",&a);
4     Put(a);
5 }

第二种方法将所有元素直接排序,则一定满足堆的性质。

核心代码

 1 sort(a+1,a+n+1); 

堆排序

堆的性质保证,堆的根结点一定是堆中最小的。所以,堆常用作选出最值的优化。
最典型的例子就是堆排序了。
堆排序其实是对选择排序的优化。
选择排序的思想是每一次循环都选出最小的。

选排代码

1 for(int i=1;i<=n-1;++i)
2     for(int j=i+1;j<=n;++j)
3         if(a[i]>a[j])
4             swap(a[i],a[j]);

 

而堆排也是一样的。堆排利用堆的性质,每一次都输出根结点并删去,单次时间复杂度O(logn)。那么只需n次即可完成排序,代码如下:

 1 #include<cstdio>
 2 
 3 int Heap[1000001],Len;
 4 
 5 void Swap(int &x, int &y)
 6 {
 7     int s=x;
 8     x=y;
 9     y=s;
10 } 
11 
12 void Put(int x)
13 {
14     int Son;
15      Heap[++Len]=x;
16     Son=Len;
17     while(Len!=1&Heap[Son/2]>Heap[Son])
18     {
19         Swap(Heap[Son],Heap[Son/2]);
20         Son/=2;
21     }
22 }
23 
24 int Get()
25 {
26     int re=Heap[1],Son,Fa;
27     Heap[1]=Heap[Len];
28     Len--;
29     Fa=1;
30     while(Fa*2<=Len)
31     {
32         if(Fa*2+1>Len||Heap[Fa*2]<Heap[Fa*2+1])
33             Son=Fa*2;
34         else Son=Fa*2+1;
35         if(Heap[Fa]>Heap[Son])
36         {
37             Swap(Heap[Fa],Heap[Son]);
38             Fa=Son;
39         }
40         else
41             break;
42     }
43     return re;
44 }
45 
46 int main()
47 {
48 //    freopen("duipai.in","r",stdin);
49 //    freopen("duipai.out","w",stdout);
50     int n,a;
51     scanf("%d",&n);
52     for(int i=1;i<=n;++i){
53         scanf("%d",&a);
54         Put(a);
55     }
56     for(int i=1;i<=n-1;++i)
57         printf("%d ",Get());
58     printf("%d",Get());
59     putchar('\n');
60     fclose(stdin);fclose(stdout);
61     return 0;
62 }
posted @ 2017-01-09 20:02  hankeke303  阅读(209)  评论(0编辑  收藏  举报