序列式容器——priority_queue

     priority_queue的底层是用vector实现的,因此严格意义上讲,priority_queue属于容器适配器。在介绍序列式容器之前,先介绍heap。heap的定义(以大根heap为例)是递归定义:max heap或是一棵空树 或是具有以下性质:max heap的左右子树的所有节点都是小于根节点的,其左子树和右子树也是max heap。

     heap就是一种完全二叉树,也就是说,整棵树除了底层的叶节点之外,都是满的,而最底层的叶节点从左向右不得有空隙。

     完全二叉树整棵树内没有任何节点漏掉,这带来一个极大的好处:我们可以利用array来存储所有节点。假设用一个小技巧,将array的#0元素保留,那么完全二叉树中的某个节点位于array的i处时,其左子节点必定位于array的2i处,其右子节点必位于array的2i + 1处,其父节点必位于“i/2”处。通过这种规则可以轻易的实现出完全二叉树,但是heap需要增加或减小大小,array完成不了,所以使用vector来代替。所以实现priority_queue只需要一个vector和一组heap算法即可。

  • push_heap(Iterator first, Iterator last)算法

       first,last表示vector的首尾,调用函数push_back之前,已经把要插入的元素放到了vector的尾部(此时最后一个元素不一定符合heap的性质),执行完之后,整个vector都符合heap的性质。

       为满足完全二叉树的条件,新加入的元素一定要放在最下一层作为叶节点,并填补在由左至右的第一个空格,也就是把新元素插入在底层vector的end()处。新元素是否适合于当前的位置呢?以max heap为例,为了满足max heap的条件,我们执行一个所谓的上溯程序:将新节点拿来与其父节点比较,如果key比父节点的大,就父子对换位置。如此一直上溯,直到不需要对换或直到根节点为止。

//max heap的源码
template <typename RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last){
    //注意,此函数被调用时,新元素应已置于底部容器的最尾端
    __push_heap_aux(first, last, distance_type(first), value_type(first));
}

template <typename RandomAccessIterator, typename Distance, typename T>
inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last,
                             Distance*, T*){
    __push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
    //新值必置于底部
}

template <typename RandomAccessIterator, typename Distance, typename T>
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T         value){
    Distance parent = (holeIndex - 1) / 2;//找出父节点
    while(holeIndex > topIndex && *(first + parent) < value){
        //尚未到达顶端,且父节点小于新值(于是不符合heap的次序特性)
        *(first + holeIndex) =  *(first + parent);
        holeIndex = parent;
        parent = (holeIndex - 1) / 2;
    }
    *(first + holeIndex) = value;//完成插入操作
}
  • pop_heap(Iterator first, Iterator last)算法

       first,last表示vector的首尾,以max heap为例,执行pop_heap()函数之后,最大的元素放到了vector的尾部,注意这个时候并没有把这个元素释放掉,如果需要释放,调用vector的pop_back(),如果要显示,调用back()函数。

       操作的步骤是:把vector最后一个元素放到#0(哨兵位置);把veator的头元素,放到vector尾部(如果想删除掉,执行pop_back()即可);然后执行下溯程序:将空间节点和其较大子节点“对调”,直到找到合适的位置,到此才恢复了max heap的性质。

//最核心的调整算法,将value向下调整
template <typename RandomAccessIterator, typename Distance, typename T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value){
    Distance topindex = holeIndex;
    Distance maxChild = 2 * holeIndex + 1;//洞结点之左结点
    while(maxChild < len){
        //比较洞结点之左右两个值,然后以maxChild代表较大子节点
        if((maxChild + 1) < len && *(first + maxChild) < *(first + maxChild + 1)) ++maxChild;
        if(*(first + maxChild) < value) break;//已找到合适的位置
        *(first + holeIndex) = *(first + maxChild);
        holeIndex = maxChild;
        maxChild = 2 * holeIndex + 1;
    }
    *(first + holeIndex)  = value; 
}
  • sort_heap(Iterator first, Iterator last)算法

       first,last表示vector的首尾,如果是对max heap执行这个函数,最终得到的vector是递增的,如果是对min heap执行这个函数,最终得到的vector是递减的,这个函数的内部实现是循环调用pop_heap函数。

  • make_heap(Iterator first, Iterator last)算法

        first,last表示vector的首尾,这个算法用来将一段现有的数据转化为一个heap,内部实现是多次调用调整函数。

template <typename  RandomAAccessIterator, typename T, typename Distance>
void __make_heap(RandomAAccessIterator first, RandomAAccessIterator last, T*, Distance*){
    if(last - first < 2) return; //长度为1或0不必排序
    Distance len = last - first;
    //找出第一个需要重新排列的子树的头
    Distance parent = (len - 2) / 2;
    
    while(true){
        __adjust_heap(first, parent, len, T(*(first + parent)));
        if(parent == 0) return;//走完根节点,就结束
        parent--;
    } 
}

priority_queue

      priority_queue使用heap实现的,因为heap没有迭代器,所以priority_queue也没有迭代器,不提供遍历操作。priority_queue的底层是用vector实现的,因此严格意义上讲,priority_queue属于容器适配器。

template <typename T, typename Sequence = vector<T>, typename Compare = less<typename Sequence::value_type>>
class priority_queue{
public:
    typedef typename Sequence::value_type value_type;    
    typedef typename Sequence::size_type size_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) {}

    //以下用到的make_heap(),push_heap(),pop_heap()都是泛型算法
    //注意下面的构造函数中InputIterator是指存放数据(使用这个数据建立priority_queue)的容器的迭代器
    template <typename InputIterator>
    priority_queue(InputIterator first, InputIterator last, const Compare& x):c(first, last),comp(x){make_heap(c.begin(), c.end(), comp);}
    priority_queue(InputIterator first, InputIterator last):c(first, last){make_heap(c.begin(), c.end());}
    bool empty() const {return c.empty();}
    size_type size() const {return c.size();}
    const_reference top() const {return c.front();}
    void push(const value_type& x){
        __STL_TRY{
            //push_back是泛型算法,先把元素放到vector的底端,在排序
            c.push_back(x);
            push_heap(c.begin(), c.end(), comp);
        }
        __STL_UNWIND(c.clear());
    }
    void pop(const value_type& x){
        __STL_TRY{
            //pop_back是泛型算法,从heap内取出一个元素。它并不是真正将元素弹出,而是重排heap
            //然后再以底层容器的pop_back()将元素弹出
            
            pop_heap(c.begin(), c.end(), comp);
            c.pop_back(x);
        }
        __STL_UNWIND(c.clear());
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值