整理至侯捷《STL源码剖析》
STL中实现了先入先出(FIFO)队列和优先队列。要想使用只需包含< queue >头文件,SGI版本源码中将其实现在stl_queue.h头文件中。下面详细分析这两种队列:
先入先出队列
queue也和stack一样是一种容器适配器,底层采用其它容器适配而成。双向开口,只运行尾部插入元素,头部弹出元素,不允许遍历,则不需要实现迭代器,实现较简单,源码如下:
// Forward declarations of operators < and ==, needed for friend declaration.
//相关前置声明需要
template <class _Tp,
class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(deque<_Tp>) > //接受两个模板参数,第一个为元素类型,第二个为底层实现容器,默认为deque实现
class queue;
template <class _Tp, class _Seq>
inline bool operator==(const queue<_Tp, _Seq>&, const queue<_Tp, _Seq>&);
template <class _Tp, class _Seq>
inline bool operator<(const queue<_Tp, _Seq>&, const queue<_Tp, _Seq>&);
template <class _Tp, class _Sequence>
class queue {
// requirements:
__STL_CLASS_REQUIRES(_Tp, _Assignable);
__STL_CLASS_REQUIRES(_Sequence, _FrontInsertionSequence);
__STL_CLASS_REQUIRES(_Sequence, _BackInsertionSequence);
typedef typename _Sequence::value_type _Sequence_value_type;
__STL_CLASS_REQUIRES_SAME_TYPE(_Tp, _Sequence_value_type);
#ifdef __STL_MEMBER_TEMPLATES
template <class _Tp1, class _Seq1>
friend bool operator== (const queue<_Tp1, _Seq1>&,
const queue<_Tp1, _Seq1>&);
template <class _Tp1, class _Seq1>
friend bool operator< (const queue<_Tp1, _Seq1>&,
const queue<_Tp1, _Seq1>&);
#else /* __STL_MEMBER_TEMPLATES */
friend bool __STD_QUALIFIER
operator== __STL_NULL_TMPL_ARGS (const queue&, const queue&);
friend bool __STD_QUALIFIER
operator< __STL_NULL_TMPL_ARGS (const queue&, const queue&);
#endif /* __STL_MEMBER_TEMPLATES */
public:
//型别定义
typedef typename _Sequence::value_type value_type;
typedef typename _Sequence::size_type size_type;
typedef _Sequence container_type;
typedef typename _Sequence::reference reference;
typedef typename _Sequence::const_reference const_reference;
protected:
_Sequence c; //底层容器成员变量
public:
queue() : c() {} //构造器
explicit queue(const _Sequence& __c) : c(__c) {}
bool empty() const { return c.empty(); } //判断队列是否为空,调用底层容器相关成员函数即可
size_type size() const { return c.size(); } //返回队列大小,const函数
reference front() { return c.front(); } //访问队列头部元素
const_reference front() const { return c.front(); } //返回队列头部元素,const函数
reference back() { return c.back(); } //访问队列尾部元素
const_reference back() const { return c.back(); } //访问队列尾部元素,const函数
void push(const value_type& __x) { c.push_back(__x); } //入队操作
void pop() { c.pop_front(); } //出对操作
};
template <class _Tp, class _Sequence>
bool
operator==(const queue<_Tp, _Sequence>& __x, const queue<_Tp, _Sequence>& __y) //重载==运算符
{
return __x.c == __y.c;
}
template <class _Tp, class _Sequence>
bool
operator<(const queue<_Tp, _Sequence>& __x, const queue<_Tp, _Sequence>& __y) //重载<运算符
{
return __x.c < __y.c;
}
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template <class _Tp, class _Sequence>
bool
operator!=(const queue<_Tp, _Sequence>& __x, const queue<_Tp, _Sequence>& __y)
{
return !(__x == __y);
}
template <class _Tp, class _Sequence>
bool
operator>(const queue<_Tp, _Sequence>& __x, const queue<_Tp, _Sequence>& __y)
{
return __y < __x;
}
template <class _Tp, class _Sequence>
bool
operator<=(const queue<_Tp, _Sequence>& __x, const queue<_Tp, _Sequence>& __y)
{
return !(__y < __x);
}
template <class _Tp, class _Sequence>
bool
operator>=(const queue<_Tp, _Sequence>& __x, const queue<_Tp, _Sequence>& __y)
{
return !(__x < __y);
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
优先队列
优先队列不同于FIFO,根据入队的顺序绝对出队的顺序,而是带有优先级,任何时候都是优先级高的元素先出队。
priority_queue中通过维护一个堆来实现优先级高的总是先出队,SGI中默认维护的是也最大二叉堆(max-heap)。
template <class _Tp,
class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(vector<_Tp>),
class _Compare
__STL_DEPENDENT_DEFAULT_TMPL(less<typename _Sequence::value_type>) >
class priority_queue {
...
}
priority_queue类模板接受三个模板参数,_Tp为元素类型,_Sequence为底层实现容器,默认为vector,_Compare为比较参数,默认为小于。
template <class _Tp,
class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(vector<_Tp>),
class _Compare
__STL_DEPENDENT_DEFAULT_TMPL(less<typename _Sequence::value_type>) >
class priority_queue {
// requirements:
__STL_CLASS_REQUIRES(_Tp, _Assignable);
__STL_CLASS_REQUIRES(_Sequence, _Sequence);
__STL_CLASS_REQUIRES(_Sequence, _RandomAccessContainer);
typedef typename _Sequence::value_type _Sequence_value_type;
__STL_CLASS_REQUIRES_SAME_TYPE(_Tp, _Sequence_value_type);
__STL_CLASS_BINARY_FUNCTION_CHECK(_Compare, bool, _Tp, _Tp);
public:
//型别定义
typedef typename _Sequence::value_type value_type;
typedef typename _Sequence::size_type size_type;
typedef _Sequence container_type;
typedef typename _Sequence::reference reference;
typedef typename _Sequence::const_reference const_reference;
protected:
_Sequence c; //底层容器变量
_Compare comp; //比较变量
public:
//构造器
priority_queue() : c() {}
explicit priority_queue(const _Compare& __x) : c(), comp(__x) {}
priority_queue(const _Compare& __x, const _Sequence& __s)
: c(__s), comp(__x)
{ make_heap(c.begin(), c.end(), comp); }
#ifdef __STL_MEMBER_TEMPLATES
template <class _InputIterator>
priority_queue(_InputIterator __first, _InputIterator __last)
: c(__first, __last) { make_heap(c.begin(), c.end(), comp); }
template <class _InputIterator>
priority_queue(_InputIterator __first,
_InputIterator __last, const _Compare& __x)
: c(__first, __last), comp(__x)
{ make_heap(c.begin(), c.end(), comp); }
template <class _InputIterator>
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, const _Sequence& __s)
: c(__s), comp(__x)
{
c.insert(c.end(), __first, __last);
make_heap(c.begin(), c.end(), comp);
}
#else /* __STL_MEMBER_TEMPLATES */
priority_queue(const value_type* __first, const value_type* __last)
: c(__first, __last) { make_heap(c.begin(), c.end(), comp); }
priority_queue(const value_type* __first, const value_type* __last,
const _Compare& __x)
: c(__first, __last), comp(__x)
{ make_heap(c.begin(), c.end(), comp); }
priority_queue(const value_type* __first, const value_type* __last,
const _Compare& __x, const _Sequence& __c)
: c(__c), comp(__x)
{
c.insert(c.end(), __first, __last);
make_heap(c.begin(), c.end(), comp);
}
#endif /* __STL_MEMBER_TEMPLATES */
bool empty() const { return c.empty(); } //判断优先队列是否为空
size_type size() const { return c.size(); } //返回优先队列大小,const函数
const_reference top() const { return c.front(); } //访问优先队列头部元素,const函数,即优先级最高元素
void push(const value_type& __x) { //入队
__STL_TRY { //实现commit or rollback语意
c.push_back(__x); //元素加入底层容器
push_heap(c.begin(), c.end(), comp); //元素调整到二叉堆中
}
__STL_UNWIND(c.clear());
}
void pop() { //出队
__STL_TRY { //commit or rollback
pop_heap(c.begin(), c.end(), comp); //元素调整出二叉堆
c.pop_back(); //元素弹出容器
}
__STL_UNWIND(c.clear());
}
};
二叉堆
二叉堆是一种完全二叉树,由于完全二叉树没有漏洞,所以我们可以用数组来存储节点,而且满足下面的性质:假设数组中索引为0空间保留不放置元素,如果完全二叉树中的某个节点位于数组中索引为i的元素,则其左子节点位于2i出,右子节点位于2i+1处,父节点位于[i/2](向下取整)处。
这样的画我们就可以用一个数组来表示二叉树,则称之为隐式表示法。
SGI实现中使用的是vector动态数组来存放节点。
二叉堆分为最大二叉堆(max-heap)和最小二叉堆(min-heap),max-heap中每个节点键值都大于等于其子节点,min-heap反之,SGI实现中提供的是最大二叉堆。
具体二叉堆的操作实现在stl_heap.h头文件中,包括push_heap,pop_heap,sort_heap,和make_heap操作。
- push_heap
新加入元素首先加到底层容器的尾部,即vector的尾部,也即二叉树中的叶子节点中从左到右的第一个位置,然后执行上溯程序,依次向上比较,键值大于父节点则调换,直到不需调换为止。
template <class _RandomAccessIterator>
inline void
push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
__STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
_LessThanComparable);
__push_heap_aux(__first, __last,
__DISTANCE_TYPE(__first), __VALUE_TYPE(__first)); //此函数被调用时,新元素已置于底层容器的最尾端
}
template <class _RandomAccessIterator, class _Distance, class _Tp>
inline void
__push_heap_aux(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Distance*, _Tp*)
{
__push_heap(__first, _Distance((__last - __first) - 1), _Distance(0),
_Tp(*(__last - 1))); //新值置于容器尾端,即二叉树第一个洞号:(last-first)-1
}
template <class _RandomAccessIterator, class _Distance, class _Tp>
void
__push_heap(_RandomAccessIterator __first,
_Distance __holeIndex, _Distance __topIndex, _Tp __value)
{
_Distance __parent = (__holeIndex - 1) / 2; //找出其父节点
while (__holeIndex > __topIndex && *(__first + __parent) < __value) { //未达到根节点,且且父节点小于新值
*(__first + __holeIndex) = *(__first + __parent); //令洞值为父值
__holeIndex = __parent; //调整洞号,向上调整父节点
__parent = (__holeIndex - 1) / 2; //新洞的父节点
}
*(__first + __holeIndex) = __value; //令洞值为新值,完成插入
}
template <class _RandomAccessIterator, class _Distance, class _Tp,
class _Compare> //允许指定比较操作版本
void
__push_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __topIndex, _Tp __value, _Compare __comp)
{
_Distance __parent = (__holeIndex - 1) / 2;
while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) {
*(__first + __holeIndex) = *(__first + __parent);
__holeIndex = __parent;
__parent = (__holeIndex - 1) / 2;
}
*(__first + __holeIndex) = __value;
}
template <class _RandomAccessIterator, class _Compare,
class _Distance, class _Tp>
inline void
__push_heap_aux(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Compare __comp,
_Distance*, _Tp*)
{
__push_heap(__first, _Distance((__last - __first) - 1), _Distance(0),
_Tp(*(__last - 1)), __comp);
}
template <class _RandomAccessIterator, class _Compare>
inline void
push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
_Compare __comp)
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
__push_heap_aux(__first, __last, __comp,
__DISTANCE_TYPE(__first), __VALUE_TYPE(__first));
}
- pop_heap
出队操作,由前面分析可知,出队的元素为底层容器中的第一个元素,将其覆盖最后一个元素值(保存之前的值),则客户端可以执行底层容器操作pop_back取走这个值;下面重新调整二叉堆,使之满足要求,具体操作为:从洞号为0(即树根位置)依次向下比较,满足要求位置插入之前覆盖的元素即完成操作。
template <class _RandomAccessIterator>
inline void pop_heap(_RandomAccessIterator __first,
_RandomAccessIterator __last)
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
__STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
_LessThanComparable);
__pop_heap_aux(__first, __last, __VALUE_TYPE(__first));
}
template <class _RandomAccessIterator, class _Tp>
inline void
__pop_heap_aux(_RandomAccessIterator __first, _RandomAccessIterator __last,
_Tp*)
{
__pop_heap(__first, __last - 1, __last - 1,
_Tp(*(__last - 1)), __DISTANCE_TYPE(__first));
}
template <class _RandomAccessIterator, class _Tp, class _Distance>
inline void
__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
_RandomAccessIterator __result, _Tp __value, _Distance*)
{
*__result = *__first; //设定尾值为第一个元素值,即为将要出队的元素,可以由底层容器执行pop_back弹出
__adjust_heap(__first, _Distance(0), _Distance(__last - __first), __value); //重新调整二叉堆,洞号为0(即树根处),欲调整值为value*(即原先尾端的元素)
}
template <class _RandomAccessIterator, class _Distance, class _Tp>
void
__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __len, _Tp __value)
{
_Distance __topIndex = __holeIndex;
_Distance __secondChild = 2 * __holeIndex + 2; //洞节点的右子节点
while (__secondChild < __len) {
if (*(__first + __secondChild) < *(__first + (__secondChild - 1))) //比较洞节点左右两个子值
__secondChild--; //为较大者
*(__first + __holeIndex) = *(__first + __secondChild); //令较大值为洞值
__holeIndex = __secondChild; //洞号下移指向较大者
__secondChild = 2 * (__secondChild + 1); //找出新洞号的右子节点
}
if (__secondChild == __len) { //没有右子节点,只有左子节点
*(__first + __holeIndex) = *(__first + (__secondChild - 1)); //另左子值为洞值
__holeIndex = __secondChild - 1; //洞号下移值左子节点
}
__push_heap(__first, __holeIndex, __topIndex, __value); //将欲调整值填入目前洞号
}
接受比较参数版本
template <class _RandomAccessIterator, class _Compare>
inline void
pop_heap(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Compare __comp)
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
__pop_heap_aux(__first, __last, __VALUE_TYPE(__first), __comp);
}
template <class _RandomAccessIterator, class _Tp, class _Compare>
inline void
__pop_heap_aux(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Tp*, _Compare __comp)
{
__pop_heap(__first, __last - 1, __last - 1, _Tp(*(__last - 1)), __comp,
__DISTANCE_TYPE(__first));
}
template <class _RandomAccessIterator, class _Tp, class _Compare,
class _Distance>
inline void
__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
_RandomAccessIterator __result, _Tp __value, _Compare __comp,
_Distance*)
{
*__result = *__first; //设定尾值为第一个元素值,即为将要出队的元素,可以由底层容器执行pop_back弹出
__adjust_heap(__first, _Distance(0), _Distance(__last - __first),
__value, __comp); //重新调整二叉堆,洞号为0(即树根处),欲调整值为value*(即原先尾端的元素)
}
template <class _RandomAccessIterator, class _Distance,
class _Tp, class _Compare>
void
__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
_Distance __len, _Tp __value, _Compare __comp)
{
_Distance __topIndex = __holeIndex;
_Distance __secondChild = 2 * __holeIndex + 2; //洞节点的右子节点
while (__secondChild < __len) {
if (__comp(*(__first + __secondChild), *(__first + (__secondChild - 1)))) //比较洞节点左右两个子值
__secondChild--; //为较大者
*(__first + __holeIndex) = *(__first + __secondChild); //令较大值为洞值
__holeIndex = __secondChild; //洞号下移指向较大者
__secondChild = 2 * (__secondChild + 1); //找出新洞号的右子节点
}
if (__secondChild == __len) { //没有右子节点,只有左子节点
*(__first + __holeIndex) = *(__first + (__secondChild - 1)); //另左子值为洞值
__holeIndex = __secondChild - 1; //洞号下移值左子节点
}
__push_heap(__first, __holeIndex, __topIndex, __value, __comp); //将欲调整值填入目前洞号
}
- sort_heap
由于每次出队的元素都是最大值,则将所有元素都出队后,容器中就会是一个升序的序列,则可以实现为排序算法,即堆排序。
template <class _RandomAccessIterator>
void sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
__STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
_LessThanComparable);
while (__last - __first > 1) //出队所有元素
pop_heap(__first, __last--);
}
template <class _RandomAccessIterator, class _Compare>
void
sort_heap(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Compare __comp) //接受比较参数版本
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
while (__last - __first > 1)
pop_heap(__first, __last--, __comp);
}
- make_heap
将一组数据转换为堆。
template <class _RandomAccessIterator>
inline void
make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
__STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
_LessThanComparable);
__make_heap(__first, __last,
__VALUE_TYPE(__first), __DISTANCE_TYPE(__first));
}
template <class _RandomAccessIterator, class _Tp, class _Distance>
void
__make_heap(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Tp*, _Distance*)
{
if (__last - __first < 2) return; //长度为0或1不必重新排列
_Distance __len = __last - __first;
_Distance __parent = (__len - 2)/2;
while (true) {
__adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)));
if (__parent == 0) return;
__parent--;
}
}
接受比较模板参数
template <class _RandomAccessIterator, class _Compare>
inline void
make_heap(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Compare __comp)
{
__STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
__make_heap(__first, __last, __comp,
__VALUE_TYPE(__first), __DISTANCE_TYPE(__first));
}
template <class _RandomAccessIterator, class _Compare,
class _Tp, class _Distance>
void
__make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
_Compare __comp, _Tp*, _Distance*)
{
if (__last - __first < 2) return; //长度为0或1不必重新排列
_Distance __len = __last - __first;
_Distance __parent = (__len - 2)/2;
while (true) {
__adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)),
__comp);
if (__parent == 0) return;
__parent--;
}
}