栈和队列
1、栈和队列
栈和队列都是以deque为底层容器的适配器。栈(stack)、队列(queue)、优先队列(priority_queue)这三种数据结构,准确的来说其实是一种适配器,因为底层都是以其他容器为基准。
栈:先入后出,只允许在栈顶添加和删除元素,称为入栈和出栈。
队列:先入先出,载队首取元素,在队尾添加元素,称为出队和入队。
优先队列:带权值的队列。
常见栈的应用场景包括括号问题的求解,表达式的转换和求值,函数调用和递归实现,深度优先遍历DFS等。
从源码中,可以证实stack和queue底层确实是使用deque作为底层容器封装。
stack源码:
#ifndef __STL_LIMITED_DEFAULT_TEMPLATES template <class T, class Sequence = deque<T> > #else template <class T, class Sequence> #endif class stack { 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;
queque源码
#ifndef __STL_LIMITED_DEFAULT_TEMPLATES template <class T, class Sequence = deque<T> > #else template <class T, class Sequence> #endif class 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;
2、heap 堆
heap并不是一个容器,因此它没有实现自己的迭代器,也就没有遍历操作,所以heap只是一种算法。
push_back 插入元素
插入函数:push_heap,heap只接受RandomAccessIterator类型的迭代器。
template <class RandomAccessIterator> inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) { __push_heap_aux(first, last, distance_type(first), value_type(first)); } template <class RandomAccessIterator, class Distance, class T> inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*) { // 这里传入的是两个迭代器的长度, 0, 还有最后一个数据 __push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1))); }
pop_heap 删除元素
pop操作其实并没有真正意义取删除数据,而是将数据放在最后,只是没有指向最后的元素而已,这里使用arrary也可以,毕竟美誉队数组的大小进行调整。
pop的实现有两种。
template <class RandomAccessIterator, class Compare> inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) { __pop_heap_aux(first, last, value_type(first), comp); } template <class RandomAccessIterator, class T, class Compare> inline void __pop_heap_aux(RandomAccessIterator first, RandomAccessIterator last, T*, Compare comp) { __pop_heap(first, last - 1, last - 1, T(*(last - 1)), comp, distance_type(first)); } template <class RandomAccessIterator, class T, class Compare, class Distance> inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator result, T value, Compare comp, Distance*) { *result = *first; __adjust_heap(first, Distance(0), Distance(last - first), value, comp); } template <class RandomAccessIterator, class T, class Distance> inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator result, T value, Distance*) { *result = *first; // 因为这里是大根堆, 所以first的值就是最大值, 先将最大值保存. __adjust_heap(first, Distance(0), Distance(last - first), value); }
make_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*) { if (last - first < 2) return; // 计算长度, 并找出中间的根值 Distance len = last - first; Distance parent = (len - 2)/2; while (true) { // 一个个进行调整, 放到后面 __adjust_heap(first, parent, len, T(*(first + parent))); if (parent == 0) return; parent--; } }
sort_heap实现堆排序
实际操作是每次将第一位数据弹出从而实现排序功能。
template <class RandomAccessIterator> void sort_heap(RandomAccessIterator first, RandomAccessIterator last) { while (last - first > 1) pop_heap(first, last--); } template <class RandomAccessIterator, class Compare> void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) { while (last - first > 1) pop_heap(first, last--, comp); }
3、优先队列priority_queue
priority_queue是一种带权值的优先队列,支持插入和删除操作,也只能从尾部插入、头部删除,并且其顺序也并非是根据加入的顺序排列。因为优先队列也是一种队列,所以它也就跟队列一样不能直接遍历数组,也没有迭代器。
priority_queque本身也不算是一种容器,它是以vector为容器以heap为数据操作的配置器。
类型定义:
#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: // 符合traits编程规范 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; // 定义vector容器的对象 Compare comp; // 定义比较函数(伪函数) ... };
属性获取
priority_queue只有简单的3个熟悉获取函数,其本身的操作也简单,指向实现依赖了vector和heap就变得比较复杂。
class priority_queue { ... public: bool empty() const { return c.empty(); } size_type size() const { return c.size(); } const_reference top() const { return c.front(); } ... };
push和pop实现
push和pop都是采用heap算法。
priority_queue本身是很复杂的,但是当我们了解vector和heap之后再来分析,它就变得简单了:即将vector作为容器,heap作为算法来操作的配置器。同时,这也体现了STL的灵活性:通过各种容器与算法的结合就能实现另一种功能。
最后,在生产环境中,stl所有的容器用法都有一个原则,即:避免拷贝开销,不要直接吧大的数据对象直接往容器里塞,而是使用指针。