stl源码分析之priority queue
前面两篇介绍了gcc4.8的vector和list的源码实现,这是stl最常用了两种序列式容器。除了容器之外,stl还提供了一种借助容器实现特殊操作的组件,谓之适配器,比如stack,queue,priority queue等,本文就介绍gcc4.8的priority queue的源码实现。
顾名思义,priority queue是带有优先级的队列,所以元素必须提供<操作符,与vector和list不同,priority queue允许加入元素,但是取出时只能取出优先级最高的元素。
一、 priority queue定义
priority queue没有基类
template<typename _Tp, typename _Sequence = vector<_Tp>,
typename _Compare = less<typename _Sequence::value_type> >
class priority_queue
{
public:
typedef typename _Sequence::value_type value_type;
typedef typename _Sequence::reference reference;
typedef typename _Sequence::const_reference const_reference;
typedef typename _Sequence::size_type size_type;
typedef _Sequence container_type; protected:
_Sequence c;
_Compare comp;
…...
priority queue底层默认使用vector,含有两个成员,vector c存储数据,comp是一个仿函数,用来比较数据大小。
二、 priority queue构造方式
可以用vector直接初始化priority queue,也可以任意迭代器或者数组指针初始化。
explicit
priority_queue(const _Compare& __x,
const _Sequence& __s)
: c(__s), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); } explicit
priority_queue(const _Compare& __x = _Compare(),
_Sequence&& __s = _Sequence())
: c(std::move(__s)), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<typename _InputIterator>
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x,
const _Sequence& __s)
: c(__s), comp(__x)
{
__glibcxx_requires_valid_range(__first, __last);
c.insert(c.end(), __first, __last);
std::make_heap(c.begin(), c.end(), comp);
}
template<typename _InputIterator>
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x = _Compare(),
_Sequence&& __s = _Sequence())
: c(std::move(__s)), comp(__x)
{
__glibcxx_requires_valid_range(__first, __last);
c.insert(c.end(), __first, __last);
std::make_heap(c.begin(), c.end(), comp);
}
将元素全部插入priority queue后,使用 make_heap将其建成最大堆,
template<typename _RandomAccessIterator>
void make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{ typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;
typedef typename iterator_traits<_RandomAccessIterator>::difference_type
_DistanceType;
if (__last - __first < )
return;
const _DistanceType __len = __last - __first;
_DistanceType __parent = (__len - ) / ;
while (true)
{
_ValueType __value = _GLIBCXX_MOVE(*(__first + __parent));
std::__adjust_heap(__first, __parent, __len, _GLIBCXX_MOVE(__value));
if (__parent == )
return;
__parent--;
}
}
__adjust_heap是一个下溯过程,从最后一个非叶子节点往前一个个执行下溯过程,使得以其为根节点的子树是一个最大堆。
template<typename _RandomAccessIterator, typename _Distance,
typename _Tp, typename _Compare>
void __adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __len, _Tp __value, _Compare __comp)
{
const _Distance __topIndex = __holeIndex;
_Distance __secondChild = __holeIndex;
while (__secondChild < (__len - ) / )
{
__secondChild = * (__secondChild + );
if (__comp(*(__first + __secondChild),
*(__first + (__secondChild - ))))
__secondChild--;
*(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __secondChild));
__holeIndex = __secondChild;
}
if ((__len & ) == && __secondChild == (__len - ) / )
{
__secondChild = * (__secondChild + );
*(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first
+ (__secondChild - )));
__holeIndex = __secondChild - ;
}
std::__push_heap(__first, __holeIndex, __topIndex,
_GLIBCXX_MOVE(__value), __comp);
}
三、 priority queue的元素操作
priority queue只有push和pop两个主要操作,push增加新的元素,
void push(const value_type& __x)
{
c.push_back(__x);
std::push_heap(c.begin(), c.end(), comp);
}
先放到最后一个位置,再使用 push_heap执行一个上溯操作,将插入元素移动到合适位置,保证整个queue仍然是个最大堆。
template<typename _RandomAccessIterator, typename _Distance, typename _Tp>
void
__push_heap(_RandomAccessIterator __first,
_Distance __holeIndex, _Distance __topIndex, _Tp __value)
{
_Distance __parent = (__holeIndex - ) / ;
while (__holeIndex > __topIndex && *(__first + __parent) < __value)
{
*(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __parent));
__holeIndex = __parent;
__parent = (__holeIndex - ) / ;
}
*(__first + __holeIndex) = _GLIBCXX_MOVE(__value);
}
pop操作移除堆顶元素,
void pop()
{
std::pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
由于使用的是vector,如果移除第一个元素再make_heap的话代价会很大。这里先将第一个元素和最后一个元素交换,删除最后一个元素,再从第一个元素做一次下溯过程,就建成了新的最大堆。
stl源码分析之priority queue的更多相关文章
- STL源码分析《4》----Traits技术
在 STL 源码中,到处可见 Traits 的身影,其实 Traits 不是一种语法,更确切地说是一种技术. STL库中,有一个函数叫做 advance, 用来将某个迭代器(具有指针行为的一种 cla ...
- STL源码分析《3》----辅助空间不足时,如何进行归并排序
两个连在一起的序列 [first, middle) 和 [middle, last) 都已经排序, 归并排序最核心的算法就是 将 [first, middle) 和 [middle, last) 在 ...
- STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort
最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序. 平常大家写归并排序,通常写的是 递归版本..为了效率的考虑,STL库 给出了如下的 归并排序 ...
- STL源码分析读书笔记--第二章--空间配置器(allocator)
声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...
- STL 源码分析《2》----nth_element() 使用与源码分析
Select 问题: 在一个无序的数组中 找到第 n 大的元素. 思路 1: 排序,O(NlgN) 思路 2: 利用快排的 RandomizedPartition(), 平均复杂度是 O(N) 思路 ...
- stl源码分析之allocator
allocator封装了stl标准程序库的内存管理系统,标准库的string,容器,算法和部分iostream都是通过allocator分配和释放内存的.标准库的组件有一个参数指定使用的allocat ...
- STL源码分析与实现-stl_list容器
1. stl_list 介绍 今天我们来总结一下stl_List, 通过之前介绍单链表的文章,其实对链表的基本操作已经十分熟悉了,那对于stl_list,无非就是链表结构不一样,至于其中的增删改查的细 ...
- STL 源码分析六大组件-allocator
1. allocator 基本介绍 分配器(allocator))是C ++标准库的一个组件, 主要用来处理所有给定容器(vector,list,map等)内存的分配和释放.C ++标准库提供了默认使 ...
- STL源码分析之迭代器
前言 迭代器是将算法和容器两个独立的泛型进行调和的一个接口. 使我们不需要关系中间的转化是怎么样的就都能直接使用迭代器进行数据访问. 而迭代器最重要的就是对operator *和operator-&g ...
随机推荐
- Springboot中使用ibatis输出日志
logging.level.org.apache.ibatis=DEBUG logging.level.org.mybatis=DEBUG logging.level.java.sql.Connect ...
- 修改linux系统的默认语言
修改linux系统的默认语言: 1.全局修改: 所有用户都是同一种统一的语言设置 修改/etc/sysconfig/i18n文件 vi /etc/s ...
- Linux命令总结(转)
1.ls [选项] [目录名 | 列出相关目录下的所有目录和文件 -a 列出包括.a开头的隐藏文件的所有文件 -A 通-a,但不列出"."和".." -l 列出 ...
- Android——sqlite3 基本命令操作
平时用到database的地方不多,这里记录一下shell终端下直接对db的基本操作! 撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details ...
- linux内核追踪(trace)(QEMU+gdb)
1.引言 Linux内核是一个很大的模块,如果只是看源码有时会难以理解Linux内核的一些代码设计情况,如果可以结合Linux内核运行同时阅读源码再好不过,本文大致介绍Linux内核追踪方式,采用工具 ...
- 大话Linux内核中锁机制之信号量、读写信号量
大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实 ...
- oracle中的某一个用户名修改
1若修改某一个用户密码,修改用户口令格式为:alter user 用户名 identified by 新密码: 2以system 为例,想把密码修改为123456. 可输入alter user sys ...
- [笔记][SQL] 连接join
在学习菜鸟教程里的MySQL教程时,对左右连接的结果有点不解. 其中有如下两个表: runoob_tbl: +-----------+--------------+---------------+-- ...
- JAVA 设计模式之原型模式
目录 JAVA 设计模式之原型模式 简介 Java实现 1.浅拷贝 2.深拷贝 优缺点说明 1.优点 2.缺点 JAVA 设计模式之原型模式 简介 原型模式是六种创建型设计模式之一,主要应用于创建相同 ...
- PHP程序员的技术成长规划 第二阶段:提高阶段
第二阶段:提高阶段 (中级PHP程序员) 重点:提高针对LNMP的技能,能够更全面的对LNMP有熟练的应用.目标:能够随时随地搭建好LNMP环境,快速完成常规配置:能够追查解决大部分遇到的开发和线上环 ...