Chapter 4: 序列式容器之 heap and priority queue
2017-01-12 10:20
260 查看
一: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(); } } };
相关文章推荐
- STL源码剖析-序列式容器之heap和priority_queue
- 三 序列式容器(五)priority_queue(heap)(container adapter)
- STL 源码剖析读书笔记五:序列式容器之 heap、priority_queue、slist
- 【STL源码剖析读书笔记】【第4章】序列式容器之heap和priority_queue
- 【STL源码剖析读书笔记】【第4章】序列式容器之heap和priority_queue
- STL源码剖析 - 第4章 序列式容器 - priority_queue
- Chapter 9.容器适配器stack、queue、priority_queue
- STL容器-- queue and priority_queue 用法
- STL序列式容器之优先队列——priority_queue
- PriorityQueue and Heap
- 【优先队列】【堆】STL之priority_queue、make_heap()、push_heap()、pop_heap()、容器适配器
- CLRS: heap sort and priority queue
- Chapter 4: 序列式容器之 stack 和 queue
- 【数据结构与算法基础】优先队列(二叉堆实现) / Priority Queue implemented by binary heap
- STL序列式容器之heap(注heap并不归属于stl组件)
- STL queue队列容器和priority_queue优先队列容器
- 容器适配器之priority_queue
- Heap / PriorityQueue
- STL学习——STL中的序列式容器及适配器总结(vector、list、deque、stack、queue)
- STL序列式容器 - heap