STL源码学习系列十二: 容器配接器( Priority_queue)

12 篇文章 1 订阅
12 篇文章 15 订阅

容器配接器( Priority_queue)


概述

priority_queue是拥有优先级的queue,不过它容器内的元素并不是根据加入顺序排列,而是根据用户定义的优先级进行排列。priority_queue只能在队列尾部加入元素,在头部取出元素。不能遍历容器,因此不需要自己设置迭代器。在SGI STL的源码<stl_queue.h>的class priority_queue设计中,它是基于某种容器作为底部结构的,默认容器是vector容器,用户也可以自己指定容器的类型。


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 < 2)  retur n;
    const _DistanceType __len = __last - __first;
    _DistanceType __parent = (__len - 2) / 2;
    while (true)
     {
          ValueType __value = _GLIBCXX_MOVE(*(__first + __parent));
          std::__adjust_heap(__first, __parent, __len, _GLIBCXX_MOVE(__value));
          if (__parent == 0)
            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 - 1) / 2)
        {
          __secondChild = 2 * (__secondChild + 1);
          if (__comp(*(__first + __secondChild),
                     *(__first + (__secondChild - 1))))
            __secondChild--;
          *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __secondChild));
          __holeIndex = __secondChild;
        }
      if ((__len & 1) == 0 && __secondChild == (__len - 2) / 2)
        {
          __secondChild = 2 * (__secondChild + 1);
          *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first
                                                     + (__secondChild - 1)));
          __holeIndex = __secondChild - 1;
        }
      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 - 1) / 2;
      while (__holeIndex > __topIndex && *(__first + __parent) < __value)
        {
          *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __parent));
          __holeIndex = __parent;
          __parent = (__holeIndex - 1) / 2;
        }
      *(__first + __holeIndex) = _GLIBCXX_MOVE(__value);
    }

pop操作移除堆顶元素

void pop()
{
    std::pop_heap(c.begin(), c.end(), comp);
    c.pop_back();
}

由于使用的是vector,如果移除第一个元素再make_heap的话代价会很大。这里先将第一个元素和最后一个元素交换,删除最后一个元素,再从第一个元素做一次下溯过程,就建成了新的最大堆。


End

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值