✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/fYaBd
📚专栏简介:在这个专栏中,我将会分享 C++ 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
7. 标题STL 中 list 的实现
相比于 vector 的连续线型空间,list 显得复杂许多,但是它的好处在于插入或删除都只作用于一个元素空间,因此 list 对空间的运用是十分精准的,对任何位置元素的插入和删除都是常数时间。list 不能保证节点在存储空间中连续存储,也拥有迭代器,迭代器的 “++”、“–” 操作对于的是指针的操作,list 提供的迭代器类型是双向迭代器:Bidirectional iterators。
list 节点的结构见如下源码:
template <class T>
struct __list_node{
typedef void* void_pointer;
void_pointer prev;
void_pointer next;
T data;
}
从源码可看出 list 显然是一个双向链表。list 与 vector 的另一个区别是,在插入和接合操作之后,都不会造成原迭代器失效,而 vector 可能因为空间重新配置导致迭代器失效。
此外 list 也是一个环形链表,因此只要一个指针便能完整表现整个链表。list 中 node 节点指针始终指向尾端的一个空白节点,因此是一种 “前闭后开” 的区间结构。
list 的空间管理默认采用 alloc 作为空间配置器,为了方便的以节点大小为配置单位,还定义一个 list_node_allocator 函数可一次性配置多个节点空间。
由于 list 的双向特性,其支持在头部(front)和尾部(back)两个方向进行 push 和 pop 操作,当然还支持 erase,splice,sort,merge,reverse,sort 等操作,这里不再详细阐述。
8. STL 中的 deque 的实现
vector 是单向开口(尾部)的连续线性空间,deque 则是一种双向开口的连续线性空间,虽然 vector 也可以在头尾进行元素操作,但是其头部操作的效率十分低下(主要是涉及到整体的移动)。
deque 和 vector 的最大差异一个是 deque 运行在常数时间内对头端进行元素操作,二是 deque 没有容量的概念,它是动态地以分段连续空间组合而成,可以随时增加一段新的空间并链接起来
deque 虽然也提供随机访问的迭代器,但是其迭代器并不是普通的指针,其复杂程度比 vector 高很多,因此除非必要,否则一般使用 vector 而非 deque。如果需要对 deque 排序,可以先将 deque 中的元素复制到 vector 中,利用 sort 对 vector 排序,再将结果复制回 deque。
deque 由一段一段的定量连续空间组成,一旦需要增加新的空间,只要配置一段定量连续空间拼接在头部或尾部即可,因此 deque 的最大任务是如何维护这个整体的连续性。
deque 的数据结构如下:
class deque
{
...
protected:
typedef pointer* map_pointer;//指向map指针的指针
map_pointer map;//指向map
size_type map_size;//map的大小
public:
...
iterator begin();
itertator end();
...
}
deque 内部有一个指针指向 map,map 是一小块连续空间,其中的每个元素称为一个节点 node,每个 node 都是一个指针,指向另一段较大的连续空间,称为缓冲区,这里就是 deque 中实际存放数据的区域,默认大小 512 bytes。整体结构如上图所示。
deque 的迭代器数据结构如下:
struct __deque_iterator
{
...
T* cur;//迭代器所指缓冲区当前的元素
T* first;//迭代器所指缓冲区第一个元素
T* last;//迭代器所指缓冲区最后一个元素
map_pointer node;//指向map中的node
...
}
从 deque 的迭代器数据结构可以看出,为了保持与容器联结,迭代器主要包含上述 4 个元素:
deque 迭代器的 “++”、“–” 操作是远比 vector 迭代器繁琐,其主要工作在于缓冲区边界,如何从当前缓冲区跳到另一个缓冲区,当然 deque 内部在插入元素时,如果 map 中 node 数量全部使用完,且 node 指向的缓冲区也没有多余的空间,这时会配置新的 map(2 倍于当前 +2 的数量)来容纳更多的 node,也就是可以指向更多的缓冲区。在 deque 删除元素时,也提供了元素的析构和空闲缓冲区空间的释放等机制。
9. STL 中 stack 和 queue 的实现
stack
stack(栈)是一种先进后出(First In Last Out)的数据结构,只有一个入口和出口,那就是栈顶,除了获取栈顶元素外,没有其他方法可以获取到内部的其他元素,其结构图如下:
stack 这种单向开口的数据结构很容易由双向开口的 deque 和 list 形成,只需要根据 stack 的性质对应移除某些接口即可实现,stack 的源码如下:
template <class T, class Sequence = deque<T> >
class stack
{
...
protected:
Sequence c;
public:
bool empty(){return c.empty();}
size_type size() const{return c.size();}
reference top() const {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();}
};
从 stack 的数据结构可以看出,其所有操作都是围绕 Sequence 完成,而 Sequence 默认是 deque 数据结构。stack 这种 “修改某种接口,形成另一种风貌” 的行为,称为 adapter(配接器)。常将其归类为 container adapter 而非 container。
stack 除了默认使用 deque 作为其底层容器之外,也可以使用双向开口的 list,只需要在初始化 stack 时,将 list 作为第二个参数即可。由于 stack 只能操作顶端的元素,因此其内部元素无法被访问,也不提供迭代器。
queue
queue(队列)是一种先进先出(First In First Out)的数据结构,只有一个入口和一个出口,分别位于最底端和最顶端,出口元素外,没有其他方法可以获取到内部的其他元素,其结构图如下:
类似的,queue 这种“先进先出”的数据结构很容易由双向开口的 deque 和 list 形成,只需要根据 queue 的性质对应移除某些接口即可实现,queue 的源码如下:
template <class T, class Sequence = deque<T> >
class queue
{
...
protected:
Sequence c;
public:
bool empty(){return c.empty();}
size_type size() const{return c.size();}
reference front() const {return c.front();}
const_reference front() const{return c.front();}
void push(const value_type& x){c.push_back(x);}
void pop(){c.pop_front();}
};
从 queue 的数据结构可以看出,其所有操作都也都是是围绕 Sequence 完成,Sequence 默认也是 deque 数据结构。queue 也是一类 container adapter。
同样,queue 也可以使用 list 作为底层容器,不具有遍历功能,没有迭代器。