(九)deque深度剖析、queue、stack

本文详细介绍了C++标准库中的deque容器,它是一种双端队列,支持在两端高效地插入和删除元素。deque内部采用分段连续的存储方式,由多个缓冲区组成,通过迭代器模拟连续空间。deque的插入操作通过判断插入位置来提高效率,并且在需要时动态调整缓冲区。此外,deque还可以作为queue和stack的基础,提供了高效的操作接口。
摘要由CSDN通过智能技术生成

deque的实现

  • deque是双向开口的一段空间,实质上是分段连续的
  • 具体实现如下图,先分段,再将它们串接起来。底层是一个vector,存放指向每一个缓冲区的指针,缓冲区的次序与vector的次序相同。
  • 最后一个缓冲区已经被元素填满,再分配一个缓冲区,把它串接到底层的vector中,就可继续向后扩充。如果向前扩充的缓冲区也被填满,再分配一个缓冲区,vector中靠近左端元素的空位置存储新分配缓冲区的指针。当控制中心满了后,会扩容为原来的二倍,类似vector的动态增长。
  • deque的迭代器是一个class,它有四个元素。
    • node指向底层的vector,当这个迭代器++或–时,它就能跳到另一个缓冲区中
    • first指向当前缓冲区的头,last指向当前缓冲区的尾,二者指出缓冲区的边界。如果指针走到了当前缓冲区的边界,它就要通过node指针跳到下一个缓冲区。
    • cur是迭代器当前指向的元素(迭代器的作用就是指向一个元素)
    • 几乎所有的容器都维护了两个迭代器,分别指向头和尾,begin指向start,end指向finish
      在这里插入图片描述

G2.9版

  • deque的迭代器有4个指针,大小为16,deque有两个迭代器和两个指针,故大小为40
  • deque的迭代器要提供五个相关的类型
---------------------BufSize的计算------------------------
//如果n不为0,表示BufSize的大小由用户自定义,返回n
//如果n为0,表示BufSize的大小使用预设值
//如果sz(sizeof(value_type))小于512,返回512/s;如果sz不小于512,返回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的模板类--------------------
template<class T, class Alloc = alloc, size_t BufSize = 0>
class deque{
public:
	typedef T value_type;
	typedef __deque_iterator<T, T&, T*, BufSize> iterator;
protected:
	typedef pointer* map_pointer;//T**
protected:
	iterator start;
	iterator finish;
	map_pointer map;//类似指向指针数组的指针
	size_type map_size;//控制中心的长度
public:
	iterator begin() { return start; }//返回第一个元素的位置
	iterator end() { return finish; }//返回最后一个元素的下一个位置
	size_type size() const { return finish - start;}
...
}
------------------------deque的迭代器----------------------
template<class T, class Ref, class Ptr, size_t BifSize>
struct __deque_iterator {
	typedef random_access_iterator_tag iterator_category;//随机访问迭代器可以使用+=运算符
	typedef T value_type;
	typedef Ptr pointer;
	typedef Ref reference;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
	typedef T** map_pointer;
	typedef __deque__iterator self;

	T* cur;
	T* first;
	T* last;
	map_pointer node;
...
}
----------------deque<T>::insert---------------------
//在position处安插一个元素,其值为x
//插入动作会移动元素,insert会判断插入位置距离前端比较近还是后端比较近,从而得到更高的效率
iterator insert(iterator position, const value_type& x){
	if(position.cur == start.cur){//如果插入的是deque的最前端
		push_front(x);//
		return start;
	} else if (position.cur == end.cur) {//如果安插点是deque的最尾端
		push_back(x);//交给push_back做
		iterator tmp = finish;
		--tmp;
		return tmp;
	}else {
		return insert_aux(position, x);
	}
}

//insert_aux
template<class T, class Alloc, size_t BufSize>
class deque<T, Alloc, BufSize>::iterator
deque<T, Alloc, BufSize>::insert_sux(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, posl, front1);//元素怒搬移
	}
	else{//安插点之后的元素个数较少
		push_back(back));//在尾端加入最末元素同值的元素
		...
		copy_backward(pos, back2, back1);//元素搬移
	}
	*pos = x_copy;//在安插点上设定新值
	return pos;
}
  • deque如何模拟连续空间
  • ->依靠迭代器实现
  • deque的成员函数
reference operator[](size_type n)
{
	return start[difference_type(n)];
}

reference front()//返回第一个元素的位置
{ return *start; }

reference back()//返回最后一个元素的位置
{ //finish指的是最后一个元素的下一个位置
	iterator *tmp = finish;
	--tmp;
	return tmp;
}

size_type size() const//返回一共有多少个元素
{ return finish - start; }//-进行了操作符重载

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

在这里插入图片描述

reference operator*() const
{ return *cur; }

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

//重载操作符-,一般用于计算两个迭代器之间的距离
//分为三个部分,二者之间完整的buffer中存储的元素数,末尾buffer中的元素数,起始buffer在的元素数
difference_type
operator- (const self& x) const
{
	return difference_type(buffer_size()) * (node - x.cur - 1) + (cur - first) + (x.last - x.cur);
}
  • 迭代器的运算符重载
//修改迭代器所指的位置,是迭代器的成员函数
void set_node(map_pointer new_node)//传入修改后的位置
{
	node = new_node;//修改当前在控制中心中的位置
	first = *new_node;//修改first指向新的buffer
	last = first + difference_type(buffer_size());//修改last
}

self& operator++(){
	++cur;//切换至下一个元素
	if( cur == last){//如果抵达边界,就跳到下一个buffer的起始节点
	{
		set_node(node + 1);
		cur = first;
	}
	return *this;
}

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

self& operator--()
{
	if(cur == first){//如果当前在buffer的起始节点,就跳到前一个buffer的末端
		set_node(node - 1);
		cur = last;
	}
	--cur;
	return *this;
}

self operator--(int)
{
	self tmp = *this;
	--*this;
	return tmp;
}
self& operator+=(difference_type n){
	difference_type offset = n + (cur - first);
	if(offset >= 0 && offset < difference_type(buffer_size)))//目标位置在同一范围内
		cur += n;
	else{//目标位置不再同一范围内
		//计算offset为负时,-1是为了减少分类讨论,这样当offset等于buffer_size时,它虽然除得的结果为1,但是是归到商为0的那一类
		difference_type node_offset = offset > 0 ? offset / difference_type(buffer_size) : - difference_type((-offset - 1) / buffer_size()) - 1;
		//切换至正确的缓冲区
		set_node(node + node_offset);
		//切换至正确的元素
		cur = first + (offset - node_offset * difference_type(buffer_size()));
	}
	return *this;
}

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

self& operstor-=(difference_type n)
{ return *this += -n; }

//有两个-,前面的是两个迭代器相减,求二者之间的距离,这里的是将当前迭代器向前移动n个位置
self operator-(difference_type n) const
{
	self tmp = *this;
	retturn tmp -= n;
}

reference operator[](difference_type n) const
{ return *(*this + n); }

G4.9版

在这里插入图片描述

  • deque的大小为40
  • deque的模板参数变成了两个,取消了设置缓冲区大小的参数
  • deque的控制中心底层实现是vector,但是它每次扩容时复制是将元素copy到vector的中段,为了最大限度的保证左右两部分空槽位一样多
    在这里插入图片描述

queue

  • queue可以在deque的基础上封锁一些功能,成为新的一种容器
  • queue不用自己定义操作,它提供调用底层的deque容器完成所需操作
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;
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

  • stack内含了一个deque,它通过调用底层的deque的接口来实现功能
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;
public:
	bool empty() const{return c.empty(); }
	size_type size() const { return c.size(); }
	reference top() { return c.back(); }
	const_reference top() { return c.back(); }
	void push(const value_type& x) { c.push_back(x);}
	void pop(){ c.pop_back); }
};

stack和queue

  • list和deque都可以作为它们的底层结构
  • stack和deque都不允许遍历,也不提供iterator,也不允许insert
  • stack可以选择vector做底层容器,但是queue不能选vector做底层容器
    • vector没有pop_front
    • 编译器对于模板的检查不是全面的检查,一部分函数在使用前是不会检查的
  • stack和queue都不可以选择set和map做底层容器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值