带你深入理解STL之Stack和Queue
上一篇博客,带你深入理解STL之Deque容器中详细介绍了deque容器的源码实现方式。结合前面介绍的两个容器vector和list,在使用的过程中,我们确实要知道在什么情况下需要选择恰当的容器来满足需求和提升效率。一般选择的准则有如下几条:
- 如果需要随机访问一个容器,vector比list要好
- 如果需要经常插入和删除操作的话,list比vector要好
- 如果既要随机存取,又要关心两端数据的插入和删除,则选择deque
好了,复习完前面的知识后,开始介绍今天的两个容器stack和queue。由于stack和queue都是基于deque来实现的,所以相应的代码会比较简单,也是比较轻松易实现的,下面一起去看看吧。
stack
如果把deque比作一个管道,两头都可进可出的话,stack就是一个桶!只能一头进一头出,而且,压在下面的东西你看不到,你要是想看,只能把上面的东西拿出来再去看。
stack是一种先进后出的数据结构,允许新增元素、移除元素和取得最顶端的元素,除了最顶端,没有任何其他方法可以存取stack中的元素,也就是说stack没有遍历行为,因此,stack是没有迭代器的!!!!!
以deque为底层容器来实现stack这种数据结构,简直不能再简单,基本的操作函数都已经定义好了,deque可以为它完成所有工作。与其说stack是一种容器,倒不如说它是一种配接器,一种容器适配器。
下面我们就来看看stack的源码,真的没骗你,超级简单。
template <class T, class Sequence = deque<T> // 以deque作为缺省底层容器
class stack
{
// #define __STL_NULL_TMPL_ARGS <>
friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&);
friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const 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; // 底层容器,stack全靠它来实现
public:
// 以下函数直接调用底层容器的接口即可实现
// 判断stack是否为空
bool empty() const { return c.empty(); }
// stack中元素个数
size_type size() const { return c.size(); }
// 返回栈顶元素, 注意这里返回的是引用!!!
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
// 在栈顶追加新元素
void push(const value_type& x) { c.push_back(x); }
// 移除栈顶元素, 注意不返回元素的引用,
// 很多初学者随机用此容器时经常误认为pop()操作同时会返回栈顶元素的引用
void pop() { c.pop_back(); }
};
// 判断两个stack是否相等, 就要测试其内部维护容器是否相等
// x.c == y.c会调用容器重载的operator ==
template <class T, class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
return x.c == y.c;
}
// 比较两个迭代器的大小,即比较底层容器的大小
template <class T, class Sequence>
bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
return x.c < y.c;
}
你没有看错,stack的源码就只有上面几句话,全是调用底层容器的接口。下面再来看看它的同胞queue,同样很简单。
queue
queue是一种先进先出的数据结构,上面说道,dequeu是个双向可进可出的管道,stack是一个桶,queue就是一个单向的水管,只能一端进,一端出。
queue允许新增元素、移除元素、从最底端插入元素,从最顶端取得元素,但是,从了最底端插入,最顶端取出之外,没有任何其他方法可以存取queue里面的元素,queue和stack一样,不允许有遍历行为,因此,queue也没有迭代器!!!!
queue和stack一样,也是一种容器适配器,只需要调用底层容器的接口就能实现。下面来看看它的源码吧。
template <class T, class Sequence = deque<T> >
class queue
{
friend bool operator== __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
friend bool operator< __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
public:
// 由于queue仅支持对队头和队尾的操作, 所以不定义STL要求的
// pointer, iterator, difference_type
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; // 底层容器
public:
// 以下操作和stack一样
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
const_reference front() const { return c.front(); }
reference back() { return c.back(); }
const_reference back() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
// 重载==操作符,比较底层容器即可
template <class T, class Sequence>
bool operator==(const queue<T, Sequence>& x, const queue<T, Sequence>& y)
{
return x.c == y.c;
}
// 同上
template <class T, class Sequence>
bool operator<(const queue<T, Sequence>& x, const queue<T, Sequence>& y)
{
return x.c < y.c;
}
后记
这篇博客的两个”容器“比较容易理解,因为底层都已经学过了,只需要调用接口即可。最后再啰嗦两句,stack是一个先进后出的容器,queue是一个先进先出的容器,在使用过程中,需要根据你的需求来选择。我在刷leetcode的时候,碰到遍历二叉树的问题,基本上前、中后序遍历的非递归实现中,都会用到stack,而树的层序遍历中,会采用queue,具体的做法可以参考我的这片博文,全面剖析树的各类遍历方法,相信看完你会对stack和queue的使用有进一步的理解!
参考:
- 侯捷先生的《STL的源码剖析》
- C++ STL源码剖析
带你深入理解STL之Stack和Queue的更多相关文章
- 带你深入理解STL之Set和Map
在上一篇博客带你深入理解STL之RBTree中,讲到了STL中关于红黑树的实现,理解起来比较复杂,正所谓前人种树,后人乘凉,RBTree把树都种好了,接下来就该set和map这类关联式容器来" ...
- 带你深入理解STL之Vector容器
C++内置了数组的类型,在使用数组的时候,必须指定数组的长度,一旦配置了就不能改变了,通常我们的做法是:尽量配置一个大的空间,以免不够用,这样做的缺点是比较浪费空间,预估空间不当会引起很多不便. ST ...
- 带你深入理解STL之迭代器和Traits技法
在开始讲迭代器之前,先列举几个例子,由浅入深的来理解一下为什么要设计迭代器. //对于int类的求和函数 int sum(int *a , int n) { int sum = 0 ; for (in ...
- 带你深入理解STL之RBTree
最近一直忙于校招的笔试,STL的深入理解系列也耽搁了好几天,再加上!红黑树真的是超级超级难理解,超级超级复杂,参考了好多博客上的大神的理解才稍微明白一点,勉强入个门,下面请以一个菜鸟的角度跟着我一起学 ...
- 带你深入理解STL之Deque容器
在介绍STL的deque的容器之前,我们先来总结一下vector和list的优缺点.vector在内存中是分配一段连续的内存空间进行存储,其迭代器采用原生指针即可,因此其支持随机访问和存储,支持下标操 ...
- 带你深入理解STL之List容器
上一篇博客中介绍的vector和数组类似,它拥有一段连续的内存空间,并且起始地址不变,很好的支持了随机存取,但由于是连续空间,所以在中间进行插入.删除等操作时都造成了内存块的拷贝和移动,另外在内存空间 ...
- 带你深入理解STL之空间配置器(思维导图+源码)
前不久把STL细看了一遍,由于看得太"认真",忘了做笔记,归纳和总结这步漏掉了.于是为了加深印象,打算重看一遍,并记录下来里面的一些实现细节.方便以后能较好的复习它. 以前在项目中 ...
- C++ STL:stack和queue
http://blog.csdn.net/wallwind/article/details/6858634 http://blog.csdn.net/chao_xun/article/details/ ...
- [STL]deque和stack、queue
怎么说呢,deque是一种双向开口的连续线性空间,至少逻辑上看上去是这样.然而事实上却没有那么简单,准确来说deque其实是一种分段连续空间,因此其实现以及各种操作比vector复杂的多. 一.deq ...
随机推荐
- [HNOI 2003]激光炸弹
Description 一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标.现在地图上有n个目标,用整数,表示目标在地图上的位置,每个目标都有一个价值.激光炸弹的投放是通过卫星定位的,但其 ...
- 栅栏(fence)
[问题描述]小 v 家有一条栅栏,由 n 个木板顺序组成,第 i 个木板的高度是 Ai.现在小镇上流行在栅栏上画矩形,所以小 v 也要在自家的栅栏上画.若要在区间[x,x+k-1]这个区间画一个宽度为 ...
- UVALive - 3530:Martian Mining
dp 可以发现,对于(i,j),要么把它运上去,那么把它运到左边,枚举一下即可 #include<cstdio> #include<cstdlib> #include<a ...
- 51 nod 1515 明辨是非(并查集合并)
1515 明辨是非题目来源: 原创基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题 给n组操作,每组操作形式为x y p. 当p为1时,如果第x变量和第y个变量可以 ...
- [模版]平衡树splay2
题目描述 1. 加入:一个新的成员加入同好会,我会分配给他一个没有使用的id,并且询问他的兴趣值val. 2. 修改:id在区间[a,b]内的成员,兴趣值同时改变k,k有可能是负数,表示他们失去了对同 ...
- bzoj 1407: [Noi2002]Savage
Description 解题报告: 因为给定答案范围,暴力枚举时间,然后再两两枚举野人,判断是否有可能在某一年相遇,我们设这一年为\(x\),那么显然相交的条件是: \(x*(p[i]-p[j])+y ...
- [HNOI2009]最小圈
题目描述 对于一张有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过k个节点,那么一个圈的平均值为圈上k条边权的和除以k,现要求其中的最小值 输入输出格式 输入格式: 第一行2个正整数,分别为 ...
- 一日一练-JS toString 和valueOf 方法的联系与区别
子曰:类型转换中toString 和valueOf 的联系与区别分析 首先是看看ES5 的规范是如何进行说明的 在这里有几个基础知识点需要了解一下: [[Class]] [[Class]] 属于Obj ...
- TeamForge使用指南
1.什么是TeamForge 可以把TeamForge简单的理解为另外一种github 2.TeamForge的地址 与Project有关,一般会有明确的Link 3.TeamForge登录 用户名和 ...
- JVM中判断对象是否存活的方法
Java中几乎所有的对象实例都存放在堆中,在垃圾收集器对堆内存进行回收前,第一件事情就是要确定哪些对象还“存活”,哪些对象已经“死去”(即不可能再通过任何途径被使用). 引用计数算法 首先需要声明,至 ...