码迷,mamicode.com
首页 > 其他好文 > 详细

手动实现最小堆和最大堆(优先队列)

时间:2019-01-11 18:05:06      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:优先队列   一个   bre   比较   swap   soft   display   lse   dijkstra   

当我们要在一组数据中找到最小/大值或者前K大/小值的时候,我们可以使用传统的遍历方法。那么这个时候时间复杂度就是$O(N^2)$,但我们可以使用"堆"来进行优化,我们可以把找到最小/大值的复杂度降低到$O(logN)$。插入一个新值的复杂度也是$O(logN)$。

 

维护一个堆关键的就是向下维护和向上维护,基于这两种方法我们就可以实现插入,删除

向下调整,时间复杂度:$O(logn)$

建堆,时间复杂度:$O(n)$

技术分享图片
 1 //建立大根堆 
 2 
 3 const int maxn = 100;
 4 //heap为堆,n为元素个数
 5 int heap[maxn],n=10;
 6 
 7 void downAdjust(int low,int high)
 8 {
 9     int i = low, j = i*2;
10     while(j <= high)
11     {
12         if(j+1<=high && heap[j+1]>heap[j])
13             ++j;
14         if(heap[j] > heap[i])
15         {
16             swap(heap[j],heap[i]);
17             i = j;
18             j = i*2;    
19         }
20         else
21             break;    
22     }
23 } 
24 
25 void createHeap()
26 {
27     for(int i=n/2;i>=1;--i)
28     {
29         downAdjust(i,n);
30     }
31 }
View Code

 

完全二叉树的叶子结点个数为$\biggl\lceil\frac{n}{2}\biggr\rceil\qquad$,因此数组下标在$[1,\biggl\lfloor\frac{n}{2}\biggr\rfloor]$范围内的结点都是非叶子结点。于是可以从$\biggl\lfloor\frac{n}{2}\biggr\rfloor]$号位开始倒着枚举结点。倒着枚举保证了每个结点都是以其为根节点的子树中权值最大的结点。

删除堆顶元素 时间复杂度:$O(logn)$

技术分享图片
 1 void deleteTop() // 删除堆顶元素 时间复杂度:O(logn)
 2 {
 3     heap[1] = heap[n--];
 4     downAdjust(1,n);
 5 }
 6 向上调整,时间复杂度:O(logn)
 7 void upAdjust(int low,int high)
 8 {
 9     int i = high,j = i/2;
10     while(j>=low)
11     {
12         if(heap[j] < heap[i])
13         {
14             swap(heap[j],heap[i]);
15             i = j;
16             j = i/2;
17         }
18         else
19             break;
20     }
21 }
22 
23 void insert(int x) // 添加元素
24 {
25     heap[++n] = x;
26     upAdjust(1,n);
27 }
View Code

 

堆排序
具体实现时,为了节省空间,可以倒着遍历数组,假设当前访问到i号位,那么将堆顶元素与i号位的元素交换,接着在[1,i-1]范围内对堆顶元素进行一次向下调整

技术分享图片
1 void heapSort()
2 {
3     createHeap(); // 建堆
4     for(int i=n;i>1;--i)
5     {
6         swap(heap[i],heap[1]);
7         downAdjust(1,i-1);    
8     } 
9 }
View Code

 

以上的就是最大堆的实现,最小堆只要把判断大小换过来便是了

Dijkstra算法也可以使用堆来优化找离源点最近的顶点的过程,使时间复杂度下降到$O(M+N)logN$,堆还可以用来求一个数列中第K大的数:首先建立一个大小为K的最小堆,从第K+1个数开始,与堆顶进行比较,如果比堆顶大则代替堆顶并维护,比堆顶小则直接舍弃。这样最后堆顶便是第K大数。时间复杂度为$O(NlogK)$。

手动实现最小堆和最大堆(优先队列)

标签:优先队列   一个   bre   比较   swap   soft   display   lse   dijkstra   

原文地址:https://www.cnblogs.com/kachunyippp/p/10256387.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!