《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的算竞选手来说它是必不可少的知识储备。

点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)