在《C++ STL priority_queue适配器入门》我们就曾提到优先队列的底层结构是堆,具体是二叉堆,这个二叉堆可以类比二叉树进行学习。可能有读者存疑了,本章不是在学习容器适配器吗,怎么会谈到底层结构呢,这么说优先队列底层到底是堆还是vector容器还是deque容器呢?其实,结构和容器是两个完全不同的概念,但是它们可以组合在一起!就适配容器而言,优先队列采用vector容器或deque容器做底层容器,主要是这两个容器能够动态扩容,支持随机访问和元素连续存储;就整个结构而言,堆是一种数据结构,优先队列的堆是一个二叉堆,它是通过数组来模拟二叉树的链式结构,节点i的父节点是(i-1)/2,其左子树是i*2+1,右节点是i*2+2。
对于二叉堆来说:
如果所有父节点都大于等于他们的子节点,那我们称该二叉堆为大顶堆,反之为小顶堆。
STL库中的<algorighm>为我们准备了配置二叉堆的相关函数:
函数 | 参数 | 功能 |
---|---|---|
std::make_heap | (first, last[, comp]) | 将 [first, last) 范围内的元素构建成一个堆结构 |
std::push_heap | (first, last[, comp]) | 假设 [first, last-1) 已是堆,将 last-1 位置的元素加入到堆中并重新调整 |
std::pop_heap | (first, last[, comp]) | 将堆顶元素(first )与最后一个元素(last-1 )交换,然后对 [first, last-1) 重新建堆 |
std::sort_heap | (first, last[, comp]) | 将一个堆序列 [first, last) 转换成有序序列(升序或降序,取决于堆类型) |
std::is_heap | (first, last[, comp]) | 检查 [first, last) 范围内的元素是否满足堆结构,返回 bool |
std::is_heap_until | (first, last[, comp]) | 查找 [first, last) 中满足堆结构的最大子范围,返回结束迭代器 |
这些配置堆的函数笔者就不过多赘述了,下面让我们通过代码来简单认识它们吧:
#include<iostream> #include<deque> #include<vector> #include<algorithm>//配置堆的函数 using namespace std; /*vector容器、deque容器或queue适配器都可以做堆的底层容器*/ /*天然的公共类*/ struct cmp { bool operator()(const int& a,const int& b)const { return a>b; } }; void test() { /*进行堆的设计*/ /*存储容器*/ vector<int> v{1,4,5,2,3} ; deque<int> dq{1,4,5,2,3} ; /*make_heap构建堆结构*/ /*大根堆*/ make_heap(v.begin(),v.end()); cout <<"make_heap默认大根堆:【" <<v.front() << "】\n" ; /*小根堆*/ make_heap(dq.begin(),dq.end(),cmp());//使用到仿函数 cout <<"make_heap更改规则后实现小根堆:【"<< dq.front() << "】\n" ; /*push_heap添加元素后调整*/ v.push_back(6) ; push_heap(v.begin(),v.end());//已知v是堆的情况下重新调整堆结构 cout << "调整后保持堆结构:【" << v[0] << "】\n"; /*pop_heap()*/ pop_heap(v.begin(),v.end()) ;//移除堆顶元素,并重新维护堆 cout << "移除堆顶元素后,并重新维护堆:【" <<v[0] <<"】\n"; /*sort_heap*/ sort_heap(v.begin(),v.end()) ;//将堆进行排序,也可以sort(v.begin(),v.end()) cout << "排序后:"; for(vector<int>::iterator it = v.begin();it!=v.end();++it) cout << *it << " "; cout<< '\n' ; /*is_heap*/ /*细节:默认检查大根堆,可以添加仿函数实现小根堆*/ cout << "现在v非堆,dq为堆,分别对其进行判断:" << "【" << is_heap(v.begin(),v.end()) << "】" << " 【" << is_heap(dq.begin(),dq.end(),cmp()) << "】\n"; /*is_heap_unitl*/ /*返回区间里找到不满足堆结构的元素的下一个迭代器*/ dq.push_back(6) ; cout <<"刚插入一个元素6,由于没有push_heap()所以会返回指向6迭代器的下一个位置"<< *(is_heap_until(dq.begin(),dq.end(),cmp())-1) <<'\n' ; } int main() { test(); return 0; }
编译结果如下:
输出结果完全按照我们的指令办事!
总结:本节深度剖析了优先队列的机制,它通过采取连续存储容器+堆的数据结构来实现,是重要的适配容器,对于想要参加蓝桥杯、ACM的算竞选手来说它是必不可少的知识储备。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程