多一些Aha Time,发现技术的美妙🍺|

啊原来是这样呀

园龄:8年3个月粉丝:3关注:9

【算法基础】13.十大排序算法——堆排序

参考资料

下面这个资料展示了堆排序的数据结构、构建堆的向上向下的基本过程、构建堆的全过程、堆的弹出与压入,较为齐全

堆排序https://zhuanlan.zhihu.com/p/417623885

下面这个资料示意图较直观,但仅仅展示了堆排序

堆排序算法详解https://blog.csdn.net/qq_35344198/article/details/107067432

 

直观理解

以小根堆为例,即parent节点必小于child节点的堆。

实现目标是已经构建好的堆,不停地弹出其根节点,即可获得一个升序集合。

第一个问题在于给定无序集合,如何初始化构建堆:

(1)先使用数组来存储数据,用数组地址来索引节点

(2)为了使左右节点的索引/2就得到父节点,数组中从[1]开始存储数据,而不是常规地从[0]开始存储数据,这仅仅是为了处理方便而设计的小trick

(3)有点像归并排序。在将数据视为一个堆后,从最后一个非叶子节点A开始执行down操作(与自己的左右子节点比较大小),如果有必要的话就进行交换,交换后对发生了更新的子节点继续执行down操作,直到不再需要往下交换或没有子节点;

结束之后,再对A前一个节点展开down操作,直至到达根节点并完成对根节点的down操作;

第二个问题在于弹出堆顶元素之后如何处理:

(1)取出堆顶元素后,

(2)将堆的最后一个元素放到堆顶,即heap[0]=heap[hSize],

(3)对新的根节点进行down操作,即可重新达到新的稳定状态;

第三个问题在与对于已经构建好的堆,如何压入新元素:

(1)将新元素放至堆的最后;

(2)对新节点展开up操作,即与parent节点进行比较,如果小则互换,直至不再需要互换或到达根节点;

 

例子先行

构建堆、查看堆顶元素、弹出堆顶元素、压入新元素

复制代码
  1 #include <iostream>
  2 #include <vector>
  3 #include <algorithm>
  4 using namespace std;
  5 
  6 vector<int> heap;//用于堆存储
  7 void BuildLittileHeap(vector<int>& array);
  8 void downOp(vector<int>& array,int tgtNode);
  9 bool heapPop(int& ret);
 10 void heapPush(int val);
 11 
 12 //程序入口
 13 int main()
 14 {
 15     //初始元素
 16     vector<int> arr{55,44,33,22,11};
 17     
 18     cout<<"堆构建测试"<<endl;
 19     //输出验证
 20     BuildLittileHeap(arr);
 21     int cur=0;
 22     while(heapPop(cur)){
 23         cout<<cur<<endl;
 24     }
 25     
 26     cout<<"压入新元素测试"<<endl;
 27     BuildLittileHeap(arr);
 28     heapPush(30);
 29     //输出验证
 30     while(heapPop(cur)){
 31         cout<<cur<<endl;
 32     }
 33 
 34     return 0;
 35 }
 36 
 37 //构建小根Heap
 38 void BuildLittileHeap(vector<int>& array){
 39     //数据复制、初始化
 40     heap.clear();
 41     heap.push_back(0);//占位
 42     heap.insert(heap.end(),array.begin(),array.end());//附加
 43     int dataLen = heap.size()-1;
 44 
 45     //只需要遍历非叶子节点,注意跳过[0]
 46     for(int i = dataLen / 2; i > 0; i--){
 47         downOp(heap,i);
 48     }
 49     
 50 }
 51 
 52 //down操作
 53 void downOp(vector<int>& array,int tgtNode){
 54     int nextId = tgtNode;
 55     int dataLen=array.size()-1;
 56 
 57     //nextId始终指向三脚堆的最小的元素,注意左右节点要始终与[nextId]节点进行比较
 58     //如果左节点较小
 59     if(2*tgtNode <= dataLen && array[2*tgtNode] < array[nextId]){
 60         nextId=2*tgtNode;
 61     }
 62     //如果右节点更小
 63     if((2*tgtNode+1) <= dataLen && array[2*tgtNode+1] < array[nextId]){
 64         nextId=2*tgtNode+1;
 65     }
 66 
 67     if(nextId != tgtNode){
 68         swap(array[nextId],array[tgtNode]);//交换
 69         downOp(array,nextId);
 70     }    
 71 }
 72 
 73 //是否为空堆
 74 bool isEmpty(){
 75     //是否为空堆,仅有1个元素
 76     return heap.size()<=1;
 77 }
 78 
 79 //获取堆顶元素
 80 bool getTop(int& ret){
 81     ret=0;
 82 
 83     //是否为空堆
 84     int dataLen=heap.size()-1;
 85     if(dataLen<=0){
 86         return false;
 87     }
 88 
 89     ret=heap[1];
 90     return true;
 91 }
 92 
 93 //弹出堆顶元素
 94 bool heapPop(int& ret){
 95     //是否为空堆
 96     int dataLen=heap.size()-1;
 97     if(dataLen<=0){
 98         return false;
 99     }
100     
101     //弹出堆顶
102     ret=heap[1];
103 
104     //将最后一个元素放到堆顶
105     swap(heap[1],heap[dataLen]);
106 
107     //删除最后一个元素
108     heap.erase(heap.begin()+dataLen);
109 
110     //对堆顶的新元素进行down操作
111     downOp(heap,1);
112     
113     return true;
114 }
115 
116 //压入新元素
117 void heapPush(int val){
118     //存入最后
119     heap.push_back(val);
120     int curId=heap.size()-1;
121 
122     //up操作
123     while((curId/2)>0){
124         //中止up
125         if(heap[curId/2]<=heap[curId]){
126             break;
127         }
128 
129         //交换
130         swap(heap[curId/2],heap[curId]);    
131         curId/=2;
132     }
133 }
复制代码

 

总结提炼

1将[0]空出的小trick,方便了索引的计算,并不是必须的;

2down和up的操作要掌握;

3已经构建好的堆,不停地弹出其堆顶,即可获得一个升序/降序集合;

 

拓展方向

1优先队列

C++中标准库中的数据类型priority_queue,就是堆结构,默认为大根堆

参考资料https://zhuanlan.zhihu.com/p/478887055

本文作者:OhOfCourse

本文链接:https://www.cnblogs.com/OhOfCourse/p/16917724.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   啊原来是这样呀  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起