Chapter 4: 序列式容器之 heap and priority queue

一:heap

1:heap 概述

1):binary max heap 表示的是用户可以以任何次序将任何元素推入容器内,但是每一次取出时取出的一定是优先权最高(也就是数值最高)的元素;同样的 binary min heap 表示的每一次取出的是优先权最低的元素;

2):binary heap 是一种 complete binary tree(完全二叉树),也就是说整棵 binary tree 除了最底层的叶节点(s)之外,是填满的,而最底层的叶节点(s)由左至右又不得有空隙。对于 max heap(最大堆) 来说,每个节点的键值(key)都大于或等于其子节点键值,min heap(最小堆) 的每个节点键值(key)都小于或等于其子节点键值;

3):Complete binary tree 是基于 vector 实现的,如果 vector 的 #0 元素保留(或设为无限大值或无限小值),则当 complete binary tree 中的某个节点位于 vector 的第 i 处时,其左节点位于 vector 的 2i 处,右节点位于 vector 的 2i+1 处,父节点位于 i/2 处(除法只取整);如果 vector 的 #0 号元素不保留的话,则左节点位于 2i+1 处,右节点位于 2i+2 处,父节点位于 (i-1)/2 处。

2:heap 算法

注明:下面呈现的是最大堆(max heap)的一些算法,并且 vector 从 i=0 处开始保存数据

1):push_heap 算法

为了满足 complete binary tree 的条件,新加入的元素一定要放在 vector 的 end()处。同时为了满足 max-heap 的条件(每个节点的键值都大于或等于其子节点键值),我们需要执行一个上溯程序,将新节点拿来与其父节点比较,如果其键值(key)比父节点大,就父子对换位置,如此上溯,直到不需要对换或直到根节点为止;

下面就是push_heap算法的代码,该函数接受两个迭代器,用来表现 heap 底部容器(vector)的头尾,并且新元素已经插入到底部容器的最尾端

//迭代器 first 和 last 用来表示 vector 的头尾
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last)
{
        //注意,此函数被调用时,新元素已置于底部容器的最尾端
        __push_heap_aux(first, last, distance_type(first), value_type(first));
        //distance_type 和 value_type 均为函数,见第三章
}

template <class RandomAccessIterator, class Distance, class T>
void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*)
{
        __push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
        //根据特性:新值必置于底部容器的最尾端
}

template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value)
{
        Distance parent = (holeIndex - 1) / 2; //找出父节点
        while (holeIndex > topIndex && *(first + parent) < value) {
                *(first + holeIndex) = *(first + parent); //令洞值为父值
                holeIndex = parent;     //调整洞号,向上提升至父节点
                parent = (holeIndex - 1) / 2; //新洞的父节点
        } //持续至顶端,或满足 heap 的次序特性为止

        *(first + holeIndex) = value; //令洞值为新值,完成插入操作
}

2):pop_heap算法

pop_heap算法表示从 max heap 中取出最大值,这在 vector 中对应为第一个元素。pop_heap算法的大概操作过程为首先将 vector 的头节点值与尾值对调,然后执行下滤过程,操作代码如下:

inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last)
{
        __pop_heap_aux(first, last, value_type(first));
}

template <class RandomAccessIterator, class T>
inline void __pop_heap_aux(RandomAccessIterator first, RandomAccessIterator last, T*)
{
        __pop_heap(first, last - 1, last - 1, T*(last - 1), distance_type(first));
        //pop 操作的结果应为底部容器的第一个元素,因此,首先设定欲调整值为尾值,然后将首值
        //调至尾节点(所以上述迭代器的result 为 last - 1)。然后重整 [first, last - 1),使之重新
        //成为一个合格的 heap
}

template <class RandomAccessIterator, class Disatance, class T>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator result, T value, Distance*)
{
        *result = *first;       //设定尾值为首值,于是尾值即为欲求结果
        __adjust_heap(first, Distance(0), Distance(last - first), value);
        //以上欲重新调整 heap,洞号为 0(亦即树根处),欲调整值为 value(原尾值)
}

template <class RandomAccessIterator, class Distance, class T>
inline void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value)
{
        Distance child = 2 * holeIndex + 1;
        const Distance last_index = len - 1;
        while (child <= last_index) {
                if (child != last_index && *(first + child) < *(first + child + 1))
                        child++;
                if (value < *(first + child)) {
                        *(first + holeIndex) = *(first + child);
                        holeIndex = child;
                        child = 2 * holeIndex + 1;
                }
                else
                        break;
        }
        *(first + holeIndex) = value;
}

3):sort_heap算法

既然每次 pop_heap可获得 heap 中键值最大的元素,如果持续对整个 heap 做 pop_heap操作,每次操作范围从后向前缩减一个元素(因为pop_heap会把键值最大的元素放在底部容器的最尾端),当整个程序执行完毕时,我们便有了一个递增序列,代码如下:

template <class RanodmAccessIterator>
void sort_heap(RandomAccessIterator first, RnadomAccessIterator last)
{
        //以下,每执行一次 pop_heap(),极值(在 STL heap 中为极大值)即被放在尾端。
        //扣除尾端再执行一次 pop_heap(),次极值又被放在新尾端,一直下去,最后即地
        //排序结果
        while (last - first > 1)
                pop_heap(first, last--); //每执行 pop_heap() 一次,操作范围即退缩一格
}

4):make_heap算法

该算法用来将一段现有的数据转化为一个 heap,代码操作如下:

//将 [first, last) 排列成一个 heap
template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last)
{
        __make_heap(first, last, value_type(first), distance_type(first));
}

template <class RandomAccessIterator>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*, Distance*)
{
        if (last - first < 2)  return;
        Distance len = last - first;

        //找出第一个需要重排的子树头部,以 parent 标示出
        //由于任何叶节点都不需要执行下滤,所以有以下计算
        Distance parent = (len - 2) / 2;

        while (true) {
                //重排以 parent 为首的子树,len 是为了让 __adjust_heap()
                //判断操作范围
                __adjust_heap(first, parent, len, T(*(first + parent)));
                if (parent == 0) return;        //走完根节点,就结束
                parent--;
        }
}

二:priority_queue

1:priority_queue概述

priority_queue只允许在底端加入元素,并从顶端取出元素,每次取出的元素为优先级别最高的元素。默认情况下,priority_queue利用一个 max-heap 完成,后者是一个以 vector 表现的 complete binary tree。同样的,其也没有迭代器

2:完整代码

priority_queue默认情况下是以 vector 为底部容器的,同样的,也是 container adapter。完整代码如下:

template <class T, class Sequence = vector<T>, class 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()均为泛型算法
        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) : c(first, last)
         { make_heap(c.begin(), c.end(), comp); }

        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) {
                try {
                        //push_heap 是泛型算法,先利用底层容器的push_back()将新元素
                        //推入末端,再重排 heap
                        c.push_back(x);
                        push_heap(c.begin(), c.end(), comp);
                }
                catch(...) {
                        c.clear();
                }
        }
        void pop() {
                try {
                        //pop_heap 是泛型算法,从 heap 内取出一个元素,它并不是
                        //真正将元素弹出,而是重排 heap,然后再以底层容器 pop_back()取得弹出的元素
                        pop_heap(c.begin(), c.end(), comp);
                        c.pop_back();
                }
                catch(...) {
                        c.clear();         
                }
        }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值