STL_deque_stack_queue

本文详细解析了C++中的deque容器,一种支持从两端进行插入和删除的动态分段连续空间结构。讲解了迭代器的设计、deque插入元素的逻辑以及如何模拟连续空间。还介绍了queue和stack如何利用deque实现堆栈和队列功能,以及底层容器的选择限制。
摘要由CSDN通过智能技术生成

Deque

deque容器(双端队列)
​deque是一种双向开口的分段连续线性空间(对外号称连续,使用者无法感知它是分段的)。deque支持从头尾两端进行元素的插入和删除。deque没有容量的概念,因为它是动态地以分段连续空间组合而成的。随时可以增加一段新的空间并连接起来。

deque用vector存储buffer,当前vector容量满了之后,vector二倍扩容,将原始数据拷贝到新数据的中段,
使得map 可以向两端扩容
在这里插入图片描述

deque 的迭代器

deque的迭代器是一个class

  • node指向控制中心map,当迭代器++或-- 时,可以跳转到另一个分段
  • first last指向当前所在buffer的头和尾,标记缓冲区的边界,当迭代器在走到当前buffer边界,下一步需要走到其他buffer(通过node)
  • cur 当前迭代器指向的元素
template<class T,class Ref,class Ptr,size_t BufSiz>
struct __deque_iterator
{
	typedef random_access_iterator_tag iterator_category; //1
	typedef T value_type;	//2
	typedef Ptr pointer;	//3
	typedef Ref reference;	//4
	typedef size_t size_type;	
	typedef ptrdiff_t difference_type; //5
	typedef T** map_pointer;
	typedef __deque_iterator self;

	T* cur;
	T* first;
	T* last;
	map_pointer node;
	...
};

deque源码

template<class T,class Alloc=alloc,size_t BufSiz=0>
class deque
{
public:
	typedef T value_type;
	typedef __deque_iterator<T,T&,T*,BufSiz> iterator;

protected:
	typedef pointer* map_pointer; //T**
	iterator start;
	iterator finish;
	map_pointer map;
	size_type map_size;

public:
	iterator begin(){return start;}
	iterator end(){return end;}
	size_type size(){return finish-start;}
	...
};

​ 为了维护这种整体连续的假象,带价就是迭代器的类型会比较复杂。即采用map(类型为T**)作为主控。map实际上是一块大小连续的空间,其中每一个元素,我们称之为node,每个node都指向了另一端连续线性的空间(上图的buffer),buffer才是deque真正存储空间的地方。

​ 其中buffer大小的确定:

/*
n不为0,传回n,buffersize由user确定
n为0,buffersize使用预设值,如果sz(sizeof(value_type))小于512,传回512/sz,否则就传回1
*/
inline size_t __deque_buf_size(size_t n,size_t sz)
{
	return n!=0?n:(sz<512?size_t(512/sz):size_t(1));
}

deque插入元素

deque::insert()插入函数首先判断传入迭代器的位置是处于容器前半部分还是后半部分,再插入进比较短的那一段。

deque::insert()

iterator inset(iterator position,const value_type& x)
{
	if(position.cur==start.cur){
		push_front(x);
		return start;
	}else if(position.cur==finish.cur){
		push_back(x);
		return finish
	}else{
		return insert_aux(position,x);
	}
}

若插入位置是容器首部,则直接push_front,位置是容器尾部,则直接push_back。其他情况则调用insert_aux方法:

template<class T,class Alloc,size_t BufSize>
typename deque<T,Alloc,BufSize>::iterator 
deque<T,Alloc,BufSize>::insert_aux(iterator pos,const value_type& x){
	difference_type index = pos-start; //安插点之前的元素个数
	value_type x_copy = x;
	if(index<size()/2){			//如果安插点之前的元素个数较少
		push_front(front());	//在最前端加入和第一元素同值的元素
		...
		copy(front2,pos1,front1); //元素搬移
	}else{					//安插点之后较少
		push_back(back());	//在尾端加入和最末元素同值的元素
		...
		copy_backward(pos,back2,back2); //元素搬移
	}
	*pos = x_copy;
	return pos;
}

deque如何模拟连续的空间:

reference operator[](size_type n){
	return start[difference_type(n)];
}

reference front(){
	return *start;
}
reference back(){
    iterator tmp = finish;
    --temp;
    return *temp;
}

size_type size()const{
	return finish-start;
}

bool empty()const{
	return finish==start;
}

重要的操作符重载: -

  • 重载 判断两个迭代器之间的距离:
    两个迭代之间的buffer数量 + it1 到其尾部的长度 + it2 到其头部的长度
reference operator*()const{
   return *cur;
}

pointer operator->()const{
   return &(operator*());
}

//两根iterator之间的距离相当于  两根iterators之间的buffers的总长度+it1至其buffer尾部长度+it2至其buffer头部的长度
difference_type operator-(const self& x)const{
   return difference_type(buffer_size())*(node-x.node-1)+(cur-first)+(x.last-x.cur);
}
self& operator++(){
   ++cur;
   if(cur==last){
   	set_node(node+1); //跳到下一节点的起始节点
   	cur = first;
   }
   return *this;
}

self operator++(int){
   self tmp = *this;
   ++*this;
   return temp;
}

self& operator--(){
   --cur;
   if(cur==first){
   	set_node(node-1); //跳到上一节点的尾节点
   	cur = last;
   }
   return *this;
}

self operator--(int){
   self tmp = *this;
   --*this;
   return temp;
}


void set_node(map_pointer new_node){
   node = new_node;
   first = *node;
   last = first+difference_type(buffer_size());
}

self& operator+=(difference_type n)
{
   difference_type offset = n+(cur-first);
   if(offset>=0&&offset<difference_type(buffer_size()))
   // 目标在同一buffer内
   cur +=n;
   else{
       // 目标不在同一buffer内
       difference_type node_offset = offset>0?offset/difference_type(buffer_size()):-difference_type((-offset-1)/buffer_size())-1;
       // 切换至正确的buffer内
       set_node(node+node_offset);
       // 切换至正确的元素
       cur = first+(offset-node_offset*difference_type(buffer_size()));
   }
   return *this;
}

self operator+(difference_type n){
   self tmp = *this;
   return temp+=n;
}

queue和stack默认封装了 deque
调用deque种的函数

queue

在这里插入图片描述

template<class T, class Sequence=deque<T>>
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;     // 底层容器,默认是deque<T>
public:
    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(); }
    // ...
};

stack

在这里插入图片描述

template<class T, class Sequence=deque<T> >
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;// 底层容器,默认是deque<T>
public:
    bool empty() const { return c.empty(); }
    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); }
    void pop() { c.pop_back(); }
    // ...
};

也可以指定其他容器,例如list、vector作为stack和queue的底层容器,因为它们内部也实现了对应所需要的方法。

queue<int, list<int>> q1;
for (long i = 0; i < 10; ++i) {
   q1.push(rand());
}

stack<int, list<int>> s1;
for (long i = 0; i < 10; ++i) {
   s1.push(rand());
}

stack<int, vector<int>> s2;
for (long i = 0; i < 10; ++i) {
   s2.push(rand());
}

stack 和 queue 都不允许遍历,也不提供迭代器
stack 和queue实际调用的是容器的函数,如果容器没有实现stack和queue的功能,比如map、set没有实现stack种的功能,不可以选择set或map作为stack queue的底层结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值