STL序列容器(二)
std::forward_list
概述
forward_list是c++11 STL提供的新容器类型,它也可以提供任何位置的常量时间的插入和删除操作,但是forward_list并没有提供list中类似的insert
,emplace
以及erase
操作,而是定义了insert_after
,emplace_after
以及erase_after
操作。原因和forward_list的内部实现有关。
实际上forwar_list底层是采用单向链表实现的,单向链表和双向链表的区别是:双向链表的每一个节点除了保存数据以外还保存了两根指针,分别指向前一个节点和后一个节点;而单向链表每个节点除了数据外只有一根指针指向后续节点。倘若forward_list要提供常规的insert等的操作的话,需要访问迭代器指向的节点的前一节点,这在单向链表中是十分困难的。
同样forward_list还提供特有的before_begin()
和cbefore_begin()
方法来返回第一个元素的前向迭代器,这个迭代器不能够被解引用,只是便于用作emplace_after
,insert_after
和erase_after
的参数。forward_list提供的迭代器类型是Forward Iterator,这种迭代器只能执行自加操作,不能自减。
常用接口
- 构造函数(和list基本一致)
1 | //default (1) |
- 大小
1 | bool empty() const noexcept; |
- 插入/删除元素
1 | void push_front (const value_type& val); //在forward_list的头部插入元素,头迭代器之前添加元素 |
- 获取元素或迭代器
1 | reference front(); |
- 其它操作
1 | void splice_after (const_iterator position, forward_list& fwdlst); //将fwdlst中的所有元素转移至position之后,这同时改变了两个fordward_list的大小,且不涉及任何元素的构造和析构,这就是元素的位置转移 |
std::priority_queue
概述
priority_queue也是一种容器适配器,它也是一个queue,也就是说数据只能从一端进,从另一端出。它的最大特点是内部的第一个元素总是容器中最大的元素,也就是说,从容器中取数的时候总是会取到最大的数。其声明如下:
1 | template <class T, class Container = vector<T>, |
第一个模板参数和第二个模板参数和queue一致,第三个模板参数是一个仿函数类或函数指针,它作为容器中比较元素大小的依据。Compare接受容器中的两个元素a
和b
(类型为T
),返回bool
型的变量,希望a
排在b
前则返回true
。
priority_queue只是容器的包装,底层容器必须支持以下操作:
- empty()
- size()
- front()
- push_back()
- pop_back()
标准库中的vector和deque都满足上述条件,如果没有指定底层容器,默认使用vector。priority_queue和queue一样不提供迭代器。
二叉堆(binary heap)
priority_queue底层通过heap来实现的,heap是一种特殊的数据结构,它用数组来实现了一颗完全二叉树(complete binary tree),也叫做二叉堆(binary heap)。树上的每一个结点对应数组中的一个元素,除了最底层以外,该树是完全充满的。
上面就是一个完全二叉树,heap分为max-heap和min-heap,其中max-heap实现的完全二叉树保证每个节点的值大于它的两个子节点,即最大节点在根部,也就是在内部数组的第一个元素的位置;min-heap实现的完全二叉树保证每个节点的值小于它的两个子节点,最小节点在根部,也就是内部数组的第一个元素的位置。
关于如何实现一个heap中的相关算法可以参考《算法导论3th》第六章堆排序,这里不详细介绍,只做简单说明。
如上图,以max-heap为例,如果数组下标从1
开始,若将某个节点放在数组i的位置,那么其左子节点放在2i
的位置,右子节点放在2i+1
的位置,由于是完全二叉树,所有可以将所有节点放在相邻的位置。
考虑向上面的树中插入一个新的值a
,a
放在数组的最后一个元素右边,也就是完全二叉树的叶节点处,为了满足父节点比子节点大的规则,需要执行(percolate up)上溯程序,也就是将新的叶子节点和父节点比较大小,若比父节点大则父子交换顺序,层层上溯,直到不需要对换或到根节点为止。
从上面的树中取出元素(删除元素)时,由于是取出最大的元素,只需要取出二叉树的根节点即可,也就是底层数组中的第一个元素。为了满足堆的规则,需要执行(percolate down)下溯程序,将取走根节点后剩下的空间节点与其较大的子节点交换位置,一直到叶子节点为止。还有建堆和堆排序等建议查看算法导论中的介绍。
常用接口
priority_queue的接口和queue基本一致,只是出队的元素总是优先级最高的。
- 构造函数实例
1 | // constructing priority queues |
- 其它接口
1 | void push (const value_type& val); |
总结
文章比较短,主要是简单说了下forward_list和priority_queue两种序列容器的结构和接口,实际上forward_list使用并不是很多,而优先队列priority_queue在某些情况下比较有用,用这种数据结构实现排序算法也是很有名的。至此,STL中的常用序列容器就介绍完了,后面的文章会介绍STL容器中的另一类:关联式容器。
参考链接
- 《STL源码剖析》侯捷
- http://www.cplusplus.com/reference/
- 《C++ Primer 5th》
- 《算法导论 3th》