当前位置: 首页 > news >正文

C++STL之stack和queue


前言

本篇文章内容主要和适配器有关,主要会介绍stack、queue、deque、反向迭代器。


一、stack和queue

简单使用:

这两个的使用没啥可讲的,一个后进先出,一个先进先出,简单提一下使用,重点在模拟
stack
在这里插入图片描述
queue
在这里插入图片描述
这两个接口都类似,只不过队列可以拿到队首和队尾,stack是栈顶。
empty判空,size就是大小,push入,pop出

模拟

库里的:
在这里插入图片描述
在这里插入图片描述
stack和queue都是容器适配器,对已经存在的东西进行复用,可以想一想用什么来复用?库里复用的是deque,这个我们后面讲,用C语言模拟的时候就可以用顺序表或者链表,那这里就可以用vector / list,对于stack,内部容器需要支持push_back,pop_back,这个用vector效率不错,对于deque需要支持尾插和头删,vector没有头删,可以用erase(begin())但是这样效率太低,可以用list,deque的问题后面讲!!!!
模拟:

namespace myspace {template<class T, class Container = vector<T>>class stack{public:void push(const T& x){_con.push_back(x);}size_t size(){return _con.size();}T& top(){return _con.back();}void pop(){_con.pop_back();}bool empty(){return _con.empty();}private:Container _con;};//先进先出template<class T,class Container = list<T> >class queue{public:void push(const T& x){_con.push_back(x);}size_t size(){return _con.size();}T& front(){return _con.front();}T& back(){return _con.back();}void pop(){_con.pop_front();}bool empty(){return _con.empty();}private:Container _con;};
}

这里我们并不知道Container是啥,模板实例化是list就调用list的函数,是vector就调用vector的函数。

二、deque

deque到底是啥能让适配器使用的是这个呢?deque这么有用为啥不出名呢?
deque是双端队列,尾插尾删头插头插均为O(1),且支持[]访问下标,内部是随机迭代器,这么看有点像vector和list的结合体,与vector比较,头插效率高,不需要挪动数据;与list比较,空间利用率比较高,可以[]访问下标,那他有缺点吗?当然有,如果没有缺点我们还需要vector和list干嘛。

deque的底层比较复杂,所以我们只简单介绍
deque的底层是一小段一小段连续的空间拼接而成的,有一个中控器,通常是指针数组,每一个指针指向一个缓冲区,结构大致是这样
在这里插入图片描述
迭代器的实现更加复杂,这里不进行细致的讲解,因为不会。。
想了解可以看这个博客,非本人所写
deque确实有优点,但是缺点也很明显,遍历问题,遍历效率太低。
下面一段代码来检测三种容器的遍历速度:同时遍历N个元素

int main() {const int N = 1e6;std::vector<int> vec(N);std::deque<int> deq(N);std::list<int> lst;srand(time(0));for (int i = 0; i < N; ++i) {int x = rand();vec[i] = deq[i] = x;lst.push_back(x);}auto t1 = clock();for (int x : vec) {}auto t2 = clock();std::cout << "Vector: " << (t2 - t1)  << "s/\n";t1 = clock();for (int x : deq) {}t2 = clock();std::cout << "Deque: " << (t2 - t1) << "s/\n";t1 = clock();for (int x : lst) {}t2 = clock();std::cout << "List: " << (t2 - t1)<< "s/\n";return 0;
}

1e6数据量下:
在这里插入图片描述
1e7:
在这里插入图片描述
这个结果很正常,因为vector内存连续,CPU高速缓存,list最慢,内部空间不连续,缓存速度较慢。deque的速度不快,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,但是容器要经常遍历,所以deque平常使用较少,也就容器适配器会用到

deque中间插入是O(n)又不如list,所以缺点也很明显
接着测试中间插入的效率:搞了一千个元素。第一次在中间插入,之后在新插入元素的下一个位置插入,测试效率。

const int N = 1e7;
template<class T>
void Test_Time(T& _con)
{auto it = _con.begin();for (int i = 0; i < 500 && it != _con.end(); ++i) {++it;}int t1 = clock();for (int i = 0; i < N; ++i){it = _con.insert(it, i);++it;}int t2 = clock();cout << t2 - t1 << endl;
}
int main() {std::vector<int> v(1000);std::deque<int> d(1000);std::list<int> l(1000);Test_Time(v);Test_Time(d);Test_Time(l);return 0;
}

1e6的数据下:(1e7跑不出来了,vector快还是和内存连续有很大关系)
在这里插入图片描述
测试尾插:

const int N = 1e7;
template<class T>
void Test_Time(T& _con)
{int t1 = clock();for (int i = 0; i < N; ++i){_con.push_back(i);}int t2 = clock();cout << t2 - t1 << endl;
}
int main() {std::vector<int> v;std::deque<int> d;std::list<int> l;Test_Time(v);Test_Time(d);Test_Time(l);return 0;
}

在这里插入图片描述

通过这些结果我们可以得出容器适配器使用deque的原因了:
1.stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque的效率和内存使用率很高。
3.完美避开了它遍历容器效率低下和他的中间插入效率低下的特点,又完美利用了它两端插入和删除效率高效,内存利用率较list高的优点。

所以上面stack和queue的模拟都应该使用deque作为默认容器


三、反向迭代器

前面讲的vector和list和string都有反向迭代器,为什么现在才讲呢?因为反向迭代器的设计也利用了适配器的思想,它利用正向迭代器来实现反向迭代器并且一套模板所有带反向迭代器的都可以使用,反向迭代器++就是正向迭代器减减,反过来也是这样。

拿vector举例:
在这里插入图片描述
自己理解的确实没啥问题,rbegin在最后一个元素的位置,但是库里为什么这么设计呢?主要体现了一种对称,正向和反向正好对称,并且rbegin就是
reverse_iterator(end()),但是这样就需要控制*了,解引用实际上是解引用它的上一个元素,控制这点就行。

基本架子

里面封装一个正向迭代器,Ref和Ptr都和正向迭代器的思想类似

template<class Iterator,class Ref,class Ptr>
class Reverse_Iterator
{typedef Reverse_Iterator<Iterator, Ref, Ptr> Self;Iterator _it;
public:Reverse_Iterator(Iterator it):_it(it){}Reverse_Iterator(){}
};

类里面分装反向迭代器:

typedef Reverse_Iterator<iterator, T&, T*> reverse_iterator;
typedef Reverse_Iterator<const_iterator, const T&, const T*> const_reverse_iterator;reverse_iterator rbegin()
{return reverse_iterator(end());
}
reverse_iterator rend()
{return reverse_iterator(begin());
}
const_reverse_iterator rbegin()const
{return const_reverse_iterator(end());
}
const_reverse_iterator rend()const
{return const_reverse_iterator(begin());
}

完整

template<class Iterator,class Ref,class Ptr>
class Reverse_Iterator
{typedef Reverse_Iterator<Iterator, Ref, Ptr> Self;Iterator _it;
public:Reverse_Iterator(Iterator it):_it(it){}Reverse_Iterator(){}Ref operator*(){Iterator tmp(_it);return *(--tmp);}Ptr operator->(){return &(operator*());}Self& operator++(){--_it;return *this;}Self operator++(int){Iterator tmp(_it);--tmp;return tmp;}Self& operator--(){++_it;return *this;}Self operator--(int){Iterator tmp(_it);++tmp;return tmp;}bool operator!=(const Self& x)const{return _it != x._it;}bool operator==(const Self& x)const{return _it == x._it;}
};

这套反向迭代器非常好用,套上就可以跑。
stack_queue完整代码
反向迭代器完整代码

四、总结

本次内容主要介绍了反向迭代器和适配器,适配器相对简单,deque虽然比较复杂但是不需要过多了解,知道优缺点即可,反向迭代器是一种很牛的设计,需要记住。

http://www.dtcms.com/a/289181.html

相关文章:

  • C++string类简介
  • MySQL 配置性能优化实操指南:分版本5.7和8.0适配方案
  • 解锁Phpenv:轻松搭建PHP集成环境指南
  • C++运算符重载
  • 【智能协同云图库】第一期:用户管理接口设计与功能实现
  • 08_Opencv_基本图形绘制
  • Java SpringBoot 对接FreeSwitch
  • 【图像处理基石】什么是畸变校正?
  • AG32:解锁MCU+FPGA应用新姿势,功能与实战全解析
  • JavaScript 语言基础详解
  • 金融大前端中的 AI 应用:智能投资顾问与风险评估
  • SSE和WebSocket区别到底是什么
  • FAN-UNET:用于生物医学图像分割增强模型
  • Python 中的闭包:原理、应用与实践
  • 2025.7.20总结-实战演讲
  • 单细胞空间多组学揭示肿瘤相关成纤维细胞的保守空间亚型和细胞邻域-空间细胞亚群细分代码实现
  • 常用的三种加密算法
  • 金融工程、金融与经济学知识点
  • ICT模拟零件测试方法--电容测试
  • 算法讲解--复写零
  • 【OpenGL 渲染器开发笔记】5 顶点数据
  • LeetCode第337题_打家劫舍III
  • Spring Boot 配置文件解析
  • 《深入C++多态机制:从虚函数表到运行时类型识别》​
  • 牛客NC14661 简单的数据结构(deque双端队列)
  • python学智能算法(二十六)|SVM-拉格朗日函数构造
  • 非广告!! 【实用工具推荐】自用多功能视频播放器-РotРlayer详细图文安装使用教程
  • 【安卓笔记】RecyclerView之ItemDecoration实现吸顶效果
  • codepen使用
  • FFmpeg 图片处理