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 底部容器的头尾,并且新元素已经插入到底部容器的最尾端。如不符合这两个条件,执行结果不可预期。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 注:此函数被调用时,新元素已处于底部容器最尾端
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 的头尾。如不符合这个条件,执行结果不可预期。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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 的性质。
1
2
3
4
5
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。以下是其实现细节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#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 复杂许多,运用了继承关系,因此在型别转换上有复杂的表现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 单向链表的节点基本结构
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 的迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 单向链表的迭代器基本结构
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 的数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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内容请搜索靠谱客的其他文章。
发表评论 取消回复