STL queue和priority_queue源码解析

整理至侯捷《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--;
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值