《STL 源码剖析》 deque 实现原理

deque概述

deque是双向开口的连续线性空间,而vector是单向开口的连续线性空间。

deque没有容量的概念,它是动态地以分段连续空间组合而成,随时可以增加一点新的空间并链接起来。 不会像 vector 因 “旧空间不足,而重新配置更大空间,然后复制旧元素,再释放旧空间”。

因此deque 没有reserve功能。

deque提供 Ramdon Access Iterator,但复杂度和vector不能相提并论,这也影响到各类算法方面。因此,除非必要,应尽量使用vector。

例如,对deque进行排序,为了最高效率,可以将deque复制到vector,采用stl sort排序完,再复制回deque。

deque 是表象的连续线性空间。实际上deque 由一段一段的定量连续空间构成。deque最大任务,对多段连续空间的处理,维护整体连续的假象,并提供随机存取接口。而它的代价在于复杂的迭代器结构。

deque中控器

deque 采用一块所谓的map(非stl map)作为主控,map是一小块连续空间。每个元素(节点)都是指针,其指向一段较大的连续性空间,被称为缓冲区。缓冲区才是 deque的存储主体。

deque 迭代器和数据结构

deque 为了维持整体连续的假象,因此总体的任务就落在迭代器的 opterator ++ 和 opteartor – 身上。

其迭代器主要由四个元素构成:

  1. cur:当前指向的元素地址
  2. firest:元素所属缓冲区的头
  3. last:元素所属缓冲区的尾
  4. node:指向缓冲区所属的map的位置

在缓冲区内,迭代器遍历方式和普通迭代器相同,当进行到缓冲区边缘时,需要调用 set_node() 跳到下一个缓冲区。

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

deque数据结构主要内容

  1. map: 指向map
  2. map_size: map内有多少指针
  3. start:第一个节点的第一个元素
  4. finish:最后一个节点的最后一个元素

deque 操作原理

deque随机存储

deque 实现其随机存储的方式,总体思路上,根据总长度和每段缓冲区固定长度,来决定,要查找map中第几个node,再确定是缓冲区中,第几块内存。不过具体详细实现还是需要看源代码

例子:

  1. 假设一个缓冲区可以放入8个元素;
  2. 当deque查找[20] 元素时,20/8 + 1 = 3 (第三个node,为何+1? 是因为 map结构中 要预留头尾两个节点,用于扩充,实际元素在map 第二个node开始存储);
  3. 缓冲区第几个: 20%8 = 4(缓冲区第四个元素)

deque 插入

deque 头尾插入,push_front,push_back,在通常情况下,缓冲区剩余空间足够时,和vector类似,当不足时(只剩一个时),将会插入元素,并申请一块新的缓冲区。

deque 指定位置插入insert(iterator postion,const T& x),当postion 等于收尾时,则直接调用 push_front,push_back;当postion在中间位置时,会调用内部私有成员函数insert_aux进行实现。

insert_aux 原理,查看插入的postion前后元素数量多少,对少的的部分进行,移动式拷贝;

如果,前半部分元素少,则对前半部分的元素,进行整体前移拷贝;

如果,后半部分元素少,则对后半部分元素,进行整体后移拷贝。

由此可见,insert_aux 效率不高,由于虚构的连续空间,效率上,比vector更差。

deque 删除

deque 头尾删除,pop_front,pop_back,与插入相反,当pop的元素是缓冲区第一个或最后一个时,将删除元素后,并清除该缓冲区。

deque 指定迭代器位置删除erase(iterator pos) 原理和insert_aux类似,主要移动式拷贝的方向是缩收的。

如果,前半部分元素少,则对前半部分的元素,进行整体后移拷贝;

如果,后半部分元素少,则对后半部分元素,进行整体前移拷贝。

deque 指定范围删除erase(iterator first, iterator last) 原理上和 删除单个类似。不同在于删除的元素多少问题,以及缓冲区的释放

deque 和stack、queue的关系

stack 和 queue的实现是基于deque,只是对其操作做了限制;
由于stack和queue 对deque的操作进行进行了限制,因此在其操作效率上都比较高。
(我认为 stack和queue实际上是一个算法容器概念的逻辑,只要能符合其先进先出先进后出的逻辑。也可以用list实现)

posted @ 2022-11-18 14:52  Linese  阅读(279)  评论(0编辑  收藏  举报