C++stack和queue的模拟实现

1.stack的模拟实现

在这一部分嘞,我们不再用传统的模拟实现来实现今天要实现的内容,我们使用一种设计模式来实现今天的内容

设计模式

目前接触到的设计模式一共有两种:一种是适配器模式,一种是迭代器模式

迭代器设计模式

迭代器大家都熟悉,在string,vector,list中算是老朋友了,它为我们访问容器提供了一种统一的方式,在知道迭代器这种东西之前,我们访问数据,肯定是要知道该容器的结构细节才能访问的,比如,对于顺序表,你要知道它的底层是数组你才知道如何访问它的数据,对于链表,你要知道它的底层是一个一个的节点,你才知道要如何去访问它的数据,然而,迭代器的出现,为我们访问数据提供了一种统一的方式,它屏蔽了底层的细节

适配器设计模式

适配器大家生活中比较常见的就是电源适配器器,它的作用就相当于变压器,在不同的电压下都能够保证你的手机能够充上电,如果说迭代器的思想是封装的话,那么适配器的话就是转换,那么,让我们来看看,适配器在这一部分的应用

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口

stack的模拟实现代码

cpp 复制代码
#include<iostream>
#include<deque>
//
using namespace std;

//设计模式-适配器模式-主要是封装转换

namespace bit
{
	template<class T,class Container=deque<T>>
	class stack
	{
	public:
		void push_back(T x)
		{
			_con.push_back(x);
		}

		T& top()
		{
			return _con[_con.size()-1];
		}

		void pop()
		{
			_con.pop_back();
		}

		bool empty()
		{
			return _con.empty();
		}

		size_t size()
		{
			return _con.size();
		}
		
	private:
		Container _con;
	};

	void test_stack01()
	{
		stack<int> s;
		s.push_back(1);
		s.push_back(2);
		s.push_back(3);
		s.push_back(4);
		cout << s.size() << endl;
		while (!s.empty())
		{
			cout << s.top() << " ";
			s.pop();
		}
		cout << endl;
		
		
	}

}

上面用一个deque容器实现了一个stack,当让,这里也可以用其他的容器,比如,vector,list来实现,容器选择不唯一,这就是适配器模式,用别的容器来实现你想要的容器,肯定有人会好奇,deque是什么,那下面,我们就来介绍一下

deque

deque是我们得双向队列,什么意思嘞

从它得成员函数来看,它实现了头尾元素的插入删除,功能上是有点向vector和list的结合体,

我们知道vector的优点是:下标访问效率高

缺点是:头部插入删除需要挪动数据,效率非常低

list优点:任意位置插入删除只需要改变指针的指向,效率高

缺点:下标访问效率低

而deque刚好就结合了它们两个的优点:下标访问效率还可以,头部插入删除效率也高,那我们来看一下它的初略结构

deque的大致结构

deque的结构就是这样一个一个的bufer的数组,每个数组可以存储一定量的个数,当想要尾插数据的时候,如果数据个数达到容量的时候,就会在后面再插入一个数组,如果想要头插一个,就再前面再多插入一个数组

那它的下标访问是如何进行的,请看下图:

但是,deque的也是有缺点的

再上面的程序中,vector和deque中都有一定量的数据,对里面的数据进行排序,再计算两者排序需要用的时间,发现deque要用到的时间高出vector很多

那我们再来看一下下一个程序

再这个程序里面嘞,我们是把deque里面的数据拷贝到vecror里面,等到vector里面的数据排序完了,再把vector的数据放回deque里面 ,发现deque的排序时间、依然然高出后者的很多

那到这里我们就做一个总结:

deque的真实结构

首先,有两个迭代器,start迭代器指向第一个元素,finsh的迭代器指向最后一个元素的下一个位置

这里的operator*操作,就是取当前cur指向的元素

这里的operator++就是,当cur走到了当前数组的last位置,就该迭代器就就指向下一个node,也就是下一个数组

还有就是头插

queue的模拟实现

cpp 复制代码
#include<iostream>
#include<queue>
#include<deque>
using namespace std;

namespace bit
{
	template<class T,class Container=deque<T>>
	class queue
	{
	public:
		void push_back(T x)
		{
			_con.push_back(x);
		}

		T& top()
		{
			return _con[0];
		}

		void pop()
		{
			_con.pop_front();
		}

		bool empty()
		{
			return _con.empty();
		}
		
		size_t&size()
		{
			return _con.size();
		}



	private:
		Container _con;
	};

	void test_queue01()
	{
		queue<int> q;
		q.push_back(10);
		q.push_back(20);
		q.push_back(30);
		q.push_back(40);
		while (!q.empty())
		{
			cout << q.top()<<" ";
			q.pop();
		}
		cout << endl;

	}
}

为什么使用deque作为默认容器

tack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

  1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。

  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。

结合了deque的优点,而完美的避开了其缺陷。

相关推荐
神仙别闹6 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
XINGTECODE7 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
我们的五年16 分钟前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
zwjapple24 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five25 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
前端每日三省27 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
凡人的AI工具箱40 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
做人不要太理性43 分钟前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.1 小时前
2、桥接模式
c++·桥接模式
chnming19871 小时前
STL关联式容器之map
开发语言·c++