概述
1. heap
1.1 heap 概述
heap 并不归属 STL 容器组件,它是 priority_queue 的幕后助手。priority_queue 允许用户以任何次序将任何元素推入容器,但取出元素时一定是从优先权最高的元素开始取。binary max heap 正具有这样的性质,适合作为 priority_queue 的底层机制。
所谓 binary heap 就是一种 完全二叉树,即整棵 binary tree 除了最底层的叶节点之外,是填满的,而最底层的叶节点由左至右又不得有空隙。
完全二叉树的整棵树内没有任何节点漏洞,这带来一个极大的好处:我们可以利用 array 来储存所有节点。这种以 array 表述 tree 的方式被称为隐式表述法。
根据上述描述,实现 priority_queue 需要的工具就很简单了,一个 array 和一组 heap 算法(实现插入元素、删除元素、取出元素、将一组整数排列为一个 heap)。由于 array 大小不能扩展,可用 vector 代替。
根据元素排列方式,heap 可分为两类:max heap 和 min heap。前者每个节点键值都大于或等于其子节点键值,后者每个节点键值都小于或等于其节点键值。max heap 的最大值在根节点,并总位于底层 array 或 vector 的起头处。STL 供应的是 max heap。
1.2 heap 算法
push_heap 算法
为满足完全二叉树的条件,新加入的元素一定要放在最下一层作为叶节点,并填补在由左至右的第一个空格,即吧新元素插入在底层 vector 的 end() 处。
新元素一般并不适合其新插入的位置,为满足 max heap 的条件,需要执行一个上溯程序:将新节点与其父节点比较,如果其键值比父节点打,就对换父子位置,如此一直上溯,直到不需要对换或到根节点为止。
以下是push_heap 算法实现细节。该函数接受两个迭代器,用来表现一个 heap 底部容器的头尾,并且新元素已经插入到底部容器的最尾端。如不符合这两个条件,执行结果不可预期。
// 注:此函数被调用时,新元素已处于底部容器最尾端
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {
__push_heap_aux(first, last, distance_type(first), value_type(first));
}
// 新值置于底部容器最尾端,洞值为 (last - first) - 1
template <class RandomAccessIterator, class Distance, class 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 <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;
}
pop_heap 算法
在 max heap 中,最大元素必然在根节点。pop 取走根节点(其实是设至底部容器 vector 的尾端节点)后,为使 heap 仍然满足 max heap 的性质,必须取下最下层最右边的节点并将其值重新安插至 max heap。这时,需要执行一个下溯过程:将空间节点和其较大子节点对调,并持续下溯直到叶节点为止。然后将被取下的元素值设给这个已到达叶子层的空洞节点,再对它执行一次上溯过程。
以下是 pop_heap 的实现细节。该函数接受两个迭代器,用来表现一个 heap 的头尾。如不符合这个条件,执行结果不可预期。
template <class RandomAccessIterator>
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*) {
// 首先设定欲调整值为尾值,然后将首值调至尾节点,再重整 [first, last - 1)
__pop_heap(first, last - 1, last - 1, T(*(last - 1)), distance_type(first));
}
template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
RandomAccessIterator result, T value, Distance*) {
// 设定尾值为首值
*result = *first;
// 重整 heap,洞号为 0(即树根处),欲调整值为 value
__adjust_heap(first, Distance(0), Distance(last - first), value);
}
template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
Distance len, T value) {
Distance topIndex = holeIndex;
// 洞节点的右子节点
Distance secondChild = 2 * holeIndex + 2;
while (secondChild < len) {
// 比较洞节点的两个子节点值,以 secondChild 为较大子节点
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;
}
// 此时,可能尚未满足 max heap 的性质,需要执行一次上溯过程
__push_heap(first, holeIndex, topIndex, value);
}
注意:执行 pop_heap 后,最大元素只是被置于底部容器底端,尚未被取走,如需去其值,调用 back(),如需移除,可调用底部容器的 pop_back()。
sort_heap 算法
每次执行 pop_heap 即将 heap 中最大元素移至底部容器最尾端,若持续对整个 heap 执行 pop_heap 操作,当整个程序执行完毕时,便形成一个递增序列。
以下是 sort_heap 的实现细节。该函数接受两个迭代器,用来表现一个 heap 底部容器的头尾。如不符合这个条件,执行结果不可预期。注意:排序后,原来的 heap 就不在满足 heap 的性质。
template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
// 每执行一次 pop_heap,操作范围(last)即退缩一格
while (last - first > 1) pop_heap(first, last--);
}
make_heap 算法
make_heap 算法将一段现有的数据转化为一个 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, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
Distance*) {
// 长度为 0 或 1,不必重新调整
if (last - first < 2) return;
Distance len = last - first;
// 第一个需要重新调整的节点,即最后一个非叶节点
Distance parent = (len - 2)/2;
while (true) {
// 调整以 parent 为根的子树
__adjust_heap(first, parent, len, T(*(first + parent)));
// 直到根节点即结束调整
if (parent == 0) return;
// 已调整子树根节点的前一个节点
parent--;
}
}
2. priority_queue
2.1 priority_queue 概述
priority queue 是一个带有优先权的 queue,其内元素并非按照被推入的顺序排列,而是自动依照权值排列。权值最高者排在最前面。
缺省情况下,priority queue 利用 max heap 完成,后者以 vector 作为底部容器。
priority queue 不提供遍历功能,也不提供迭代器。
2.2 priority queue 实现
#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class T, class Sequence = vector<T>,
class Compare = less<typename Sequence::value_type> >
#else
template <class T, class Sequence, class Compare>
#endif
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) {}
#ifdef __STL_MEMBER_TEMPLATES
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); }
#else /* __STL_MEMBER_TEMPLATES */
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)
: c(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_reference top() const { return c.front(); }
void push(const value_type& x) {
__STL_TRY {
c.push_back(x);
push_heap(c.begin(), c.end(), comp);
}
__STL_UNWIND(c.clear());
}
void pop() {
__STL_TRY {
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
__STL_UNWIND(c.clear());
}
};
3. slist
STL list 是双向链表。SGI STL 另外提供了一个单向链表 slist,该容器并不在 标准规格之内。
slist 与 list 的主要区别在于:slist 的迭代器是单向的 Forward Iterator,而 list 的迭代器是双向的 Bidirectional Iterator。因此 slist 的功能受到限制,同时 slist 所耗空间更小,某些操作更快。
slist 作为单向链表,没有任何方便的办法可以回头定出前一个位置,必须从头找起。换句话说,除了 slist 起点附近的区域之外,在其他位置上采用 insert 或erase 操作函数,都属不智之举。这便是 slist 相较于 list 之下的大缺点。为此,slist 特别提供了 insert_after() 和 erase_after() 供灵活运用。
3.1 slist 的节点
slist 节点及其迭代器的设计,架构上比 list 复杂许多,运用了继承关系,因此在型别转换上有复杂的表现。
// 单向链表的节点基本结构
struct __slist_node_base
{
__slist_node_base* next;
};
// 单向链表的节点结构
template <class T>
struct __slist_node : public __slist_node_base
{
T data;
};
inline __slist_node_base* __slist_make_link(__slist_node_base* prev_node,
__slist_node_base* new_node)
{
new_node->next = prev_node->next;
prev_node->next = new_node;
return new_node;
}
inline __slist_node_base* __slist_previous(__slist_node_base* head,
const __slist_node_base* node)
{
while (head && head->next != node)
head = head->next;
return head;
}
inline const __slist_node_base* __slist_previous(const __slist_node_base* head,
const __slist_node_base* node)
{
while (head && head->next != node)
head = head->next;
return head;
}
inline void __slist_splice_after(__slist_node_base* pos,
__slist_node_base* before_first,
__slist_node_base* before_last)
{
if (pos != before_first && pos != before_last) {
__slist_node_base* first = before_first->next;
__slist_node_base* after = pos->next;
before_first->next = before_last->next;
pos->next = first;
before_last->next = after;
}
}
inline __slist_node_base* __slist_reverse(__slist_node_base* node)
{
__slist_node_base* result = node;
node = node->next;
result->next = 0;
while(node) {
__slist_node_base* next = node->next;
node->next = result;
result = node;
node = next;
}
return result;
}
3.2 slist 的迭代器
// 单向链表的迭代器基本结构
struct __slist_iterator_base
{
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef forward_iterator_tag iterator_category;
__slist_node_base* node;
__slist_iterator_base(__slist_node_base* x) : node(x) {}
// 前进一个节点
void incr() { node = node->next; }
// 比较节点是否为同一节点
bool operator==(const __slist_iterator_base& x) const {
return node == x.node;
}
bool operator!=(const __slist_iterator_base& x) const {
return node != x.node;
}
};
// 单向链表的迭代器结构
template <class T, class Ref, class Ptr>
struct __slist_iterator : public __slist_iterator_base
{
typedef __slist_iterator<T, T&, T*> iterator;
typedef __slist_iterator<T, const T&, const T*> const_iterator;
typedef __slist_iterator<T, Ref, Ptr> self;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __slist_node<T> list_node;
__slist_iterator(list_node* x) : __slist_iterator_base(x) {}
__slist_iterator() : __slist_iterator_base(0) {}
__slist_iterator(const iterator& x) : __slist_iterator_base(x.node) {}
reference operator*() const { return ((list_node*) node)->data; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
self& operator++()
{
incr();
return *this;
}
self operator++(int)
{
self tmp = *this;
incr();
return tmp;
}
};
3.3 slist 的数据结构
template <class T, class Alloc = alloc>
class slist
{
public:
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef __slist_iterator<T, T&, T*> iterator;
typedef __slist_iterator<T, const T&, const T*> const_iterator;
private:
typedef __slist_node<T> list_node;
typedef __slist_node_base list_node_base;
typedef __slist_iterator_base iterator_base;
typedef simple_alloc<list_node, Alloc> list_node_allocator;
static list_node* create_node(const value_type& x) {
// 配置空间
list_node* node = list_node_allocator::allocate();
__STL_TRY {
// 构造元素
construct(&node->data, x);
node->next = 0;
}
__STL_UNWIND(list_node_allocator::deallocate(node));
return node;
}
static void destroy_node(list_node* node) {
// 析构元素
destroy(&node->data);
// 释放空间
list_node_allocator::deallocate(node);
}
private:
list_node_base head;
public:
slist() { head.next = 0; }
slist& operator= (const slist& L);
~slist() { clear(); }
public:
iterator begin() { return iterator((list_node*)head.next); }
const_iterator begin() const { return const_iterator((list_node*)head.next);}
iterator end() { return iterator(0); }
const_iterator end() const { return const_iterator(0); }
size_type size() const { return __slist_size(head.next); }
size_type max_size() const { return size_type(-1); }
bool empty() const { return head.next == 0; }
void swap(slist& L)
{
list_node_base* tmp = head.next;
head.next = L.head.next;
L.head.next = tmp;
}
public:
friend bool operator== __STL_NULL_TMPL_ARGS(const slist<T, Alloc>& L1,
const slist<T, Alloc>& L2);
public:
reference front() { return ((list_node*) head.next)->data; }
const_reference front() const { return ((list_node*) head.next)->data; }
void push_front(const value_type& x) {
__slist_make_link(&head, create_node(x));
}
void pop_front() {
list_node* node = (list_node*) head.next;
head.next = node->next;
destroy_node(node);
}
iterator previous(const_iterator pos) {
return iterator((list_node*) __slist_previous(&head, pos.node));
}
const_iterator previous(const_iterator pos) const {
return const_iterator((list_node*) __slist_previous(&head, pos.node));
}
private:
list_node* _insert_after(list_node_base* pos, const value_type& x) {
return (list_node*) (__slist_make_link(pos, create_node(x)));
}
void _insert_after_fill(list_node_base* pos,
size_type n, const value_type& x) {
for (size_type i = 0; i < n; ++i)
pos = __slist_make_link(pos, create_node(x));
}
list_node_base* erase_after(list_node_base* pos) {
list_node* next = (list_node*) (pos->next);
list_node_base* next_next = next->next;
pos->next = next_next;
destroy_node(next);
return next_next;
}
list_node_base* erase_after(list_node_base* before_first,
list_node_base* last_node) {
list_node* cur = (list_node*) (before_first->next);
while (cur != last_node) {
list_node* tmp = cur;
cur = (list_node*) cur->next;
destroy_node(tmp);
}
before_first->next = last_node;
return last_node;
}
public:
iterator insert_after(iterator pos, const value_type& x) {
return iterator(_insert_after(pos.node, x));
}
iterator insert_after(iterator pos) {
return insert_after(pos, value_type());
}
void insert_after(iterator pos, size_type n, const value_type& x) {
_insert_after_fill(pos.node, n, x);
}
void insert_after(iterator pos, int n, const value_type& x) {
_insert_after_fill(pos.node, (size_type) n, x);
}
void insert_after(iterator pos, long n, const value_type& x) {
_insert_after_fill(pos.node, (size_type) n, x);
}
iterator insert(iterator pos, const value_type& x) {
return iterator(_insert_after(__slist_previous(&head, pos.node), x));
}
iterator insert(iterator pos) {
return iterator(_insert_after(__slist_previous(&head, pos.node),
value_type()));
}
void insert(iterator pos, size_type n, const value_type& x) {
_insert_after_fill(__slist_previous(&head, pos.node), n, x);
}
void insert(iterator pos, int n, const value_type& x) {
_insert_after_fill(__slist_previous(&head, pos.node), (size_type) n, x);
}
void insert(iterator pos, long n, const value_type& x) {
_insert_after_fill(__slist_previous(&head, pos.node), (size_type) n, x);
}
public:
iterator erase_after(iterator pos) {
return iterator((list_node*)erase_after(pos.node));
}
iterator erase_after(iterator before_first, iterator last) {
return iterator((list_node*)erase_after(before_first.node, last.node));
}
iterator erase(iterator pos) {
return (list_node*) erase_after(__slist_previous(&head, pos.node));
}
iterator erase(iterator first, iterator last) {
return (list_node*) erase_after(__slist_previous(&head, first.node),
last.node);
}
void resize(size_type new_size, const T& x);
void resize(size_type new_size) { resize(new_size, T()); }
void clear() { erase_after(&head, 0); }
public:
void reverse() { if (head.next) head.next = __slist_reverse(head.next); }
void remove(const T& val);
void unique();
void merge(slist& L);
void sort();
};
最后
以上就是眯眯眼寒风为你收集整理的STL 源码剖析读书笔记五:序列式容器之 heap、priority_queue、slist的全部内容,希望文章能够帮你解决STL 源码剖析读书笔记五:序列式容器之 heap、priority_queue、slist所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复