Deque 的理解 STL中stack与queue为什么选择使用deque为底层模板容器

目录

一、Deque的引入

二、Deque是什么?

三、deque的遍历方式?deque的缺陷?

四、它为什么能更贴合与stack与queue?

五、STL中vector与list的底层实现


一、Deque的引入

 Stack、Queue在之前的博客中我也是分别使用了更容易处理的vector和list来实现。

栈是一种先进后出,只能基于一端(栈顶元素)来进行元素访问、修改即各项操作,栈的各项操作使用vector中的一部分接口可以完整的封装起来,将vector中更倾向于stack的一些接口以栈的接口形式给用户呈现出来。例如push、pop均可以使用vector中的push_back、pop_back来进行封装。但是vector的缺陷也比较严重,就是如果插入、删除元素的时候就要进行大量的搬移元素操作。

队列是一种先进先出的数据结构,只能从尾部(队尾)进行插入元素,访问、检索、删除只能从头部(队头)来进行操作,而list链表结构头删尾插功能更加贴合了这一特性。然而list的离散式存储很容易造成内存碎片,空间利用率比较低。

上方的栈和队列都是基于一种容器来进行封装实现,将更贴合于用户需求的接口呈现出来,我们把这种容器成为容器适配器。

而在STL中的栈和队列却是基于一种叫做Deque的容器来进行封装。Deque究竟具有什么功能结构?Deque的实现原理是什么?它为什么能更贴合与stack与queue?

二、Deque是什么?

deque(双端队列)是一种双开口的“连续”的数据结构,可以在其头尾两端进行插入与删除操作,并且时间复杂度都为O(1),它相比于vector不用进行大量的元素搬移,相比于list空间利用率更高。

deque并不是真正连续的一段空间,而是由一段段连续的小空间拼接而成的。

基本原理:新建一块数组,将数组的地址(0x234)存入map中控器中,在数组中尾插的时候就在该块数组中进行尾插,当将这块数组空间存储满了之后,就开辟一块新的数组空间,并将该数组的地址(0x456)放入之前数组的下一个位置,然后在进行尾插。

当头插时,创建一块新数组,并把新数组的地址(0x123)放入map中0x234的上一块位置。然后在新数组的尾部进行插入。

 当map满载之后,将map进行扩容,开辟新空间,拷贝元素,使用新空间,这里的map中存放都是一个个的地址,数组拷贝起来也比较容易。

我的一个小疑问,为什么不使用链表来存储这些数组的地址而是用数组来进行存储呢?

首先,存放的数据是一片片的地址,扩容拷贝时也比较容易,然而使用链表来进行存放地址,链表是使用一块块小的结点来进行存储,这无疑降低了内存的使用率

三、deque的遍历方式?deque的缺陷?

void test()
{
    deque<int> q = {1,2,3,4,5,6,7};
    auto it = q.begin();
    while(it != q.end())
         cout<<*it<<" ";
    cout<<endl;
}

诸如vector、list都能实现下方类似的代码进行遍历访问,那么deque如何进行迭代器访问,上面的deque原理中一块块数组的地址通过中控器map来进行存储,然而在deque的内部迭代器上有着这样的结构:

 iterator中有这cur、first、last、node这几个指针,前三个指针就类似于组成一个vector结构,而node结点就描述了该块数组在map中的存储位置情况。

对于一整块deque的遍历来说,就是从第一个存储空间的第一个frist开始,直到最后一个存储空间的last位置,中途要进行不断的判断,deque的迭代器要不断的进行检测其是否移动到了某段空间的末尾位置,导致效率低下,这也正是deque的致命缺陷

四、它为什么能更贴合与stack与queue?

stack为先进后出结构,所有具有push_back和pop_back的容器都可以作为底层默认容器,比如list,vector。

queue为先进先出结构,所有具有push_back和pop_front的容器都可以作为底层默认容器,比如list。

然而STL库中使用deque作为底层模板容器的原因:

1、deque所有的头删头插尾删尾插的时间复杂度都为O(1)非常的高效,但是致命缺陷就是进行遍历,然而stack与queue根本不需要对元素进行遍历,使用deque作为底层模板容器简直就是取其精华去其糟粕的典型案例。

2、使用vector作为stack的底层模板虽然可以,但是效率低下(插入删除需要对元素进行大量搬移操作),使用deque相比于list作为queue的底层模板更能提高空间利用率。

五、STL中vector与list的底层实现

#include<deque>
namespace bite {
	template<class T, class Con = deque<T>>
	//template<class T, class Con = vector<T>>
	//template<class T, class Con = list<T>>
	class stack {
		public:
			stack() {}
			void push(const T& x) {
				_c.push_back(x);
			}
			void pop() {
				_c.pop_back();
			}
			T& top() {
				return _c.back();
			}
			const T& top()const {
				return _c.back();
			}
			size_t size()const {
				return _c.size();
			}
			bool empty()const {
				return _c.empty();
			}
		private:
			Con _c;
	};
}
#include<deque>
#include <list>
namespace bite {
	template<class T, class Con = deque<T>>
	//template<class T, class Con = list<T>>
	class queue {
		public:
			queue() {}
			void push(const T& x) {
				_c.push_back(x);
			}
			void pop() {
				_c.pop_front();
			}
			T& back() {
				return _c.back();
			}
			const T& back()const {
				return _c.back();
			}
			T& front() {
				return _c.front();
			}
			const T& front()const {
				return _c.front();
			}
			size_t size()const {
				return _c.size();
			}
			bool empty()const {
				return _c.empty();
			}
		private:
			Con _c;
	};
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值