目录
一. 介绍
1. stack 介绍
2. queue 介绍
二. 模拟实现
1. stack 模拟实现
2. queue 模拟实现
三. deque
1. deque 接口
2. 底层
一. 介绍
1. stack 介绍
stack(栈)是一种容器适配器,它提供了一种后进先出(LIFO)的数据结构。以下是关于stack的介绍:
- 特点:stack是一种具有特定功能的容器,它只能通过顶部(或称为栈顶)进行元素的插入和删除操作。插入操作称为入栈(push),删除操作称为出栈(pop)。对stack的访问受到限制,只能访问栈顶的元素。
- 使用容器:stack的实现通过在内部使用其他容器来存储元素。默认情况下,STL中的stack使用deque(双端队列)作为底层容器。也可以通过指定其他容器类型来创建自定义底层容器的stack。
- 基本操作:除了入栈和出栈操作外,stack还提供了其他一些常用的操作,如访问栈顶元素(top)、判断栈是否为空(empty)以及获取栈的大小(size)。
- 不支持随机访问:与向量(vector)和列表(list)等容器不同,stack不支持通过索引进行随机访问。只能访问栈顶元素,且只能进行入栈和出栈操作。
- 适用性:stack通常用于一些需要后进先出操作的场景,如逆序遍历、回溯算法、函数调用堆栈等。由于其简洁性和固定操作的特性,在许多算法和数据结构中都能够发挥作用。
总体上,stack是一种功能简单而强大的容器适配器,可用于实现后进先出的数据结构。通过提供入栈、出栈、访问栈顶等操作,stack提供了一种方便且高效的方式来管理数据。
2. queue 介绍
queue(队列)是一种容器适配器,它提供了一种先进先出(FIFO)的数据结构。以下是关于queue的介绍:
- 特点:queue是一种具有特定功能的容器,它在队尾进行元素的插入操作,而在队头进行元素的删除操作。插入操作称为入队(push),删除操作称为出队(pop)。队列中的元素按照先进先出的顺序进行处理。
- 使用容器:queue的实现通过在内部使用其他容器来存储元素。默认情况下,STL中的queue使用deque(双端队列)作为底层容器。也可以通过指定其他容器类型来创建自定义底层容器的queue。
- 基本操作:除了入队和出队操作外,queue还提供了其他一些常用的操作,如访问队头元素(front)、访问队尾元素(back)、判断队列是否为空(empty)以及获取队列的大小(size)。
- 不支持随机访问:与向量(vector)和列表(list)等容器不同,queue不支持通过索引进行随机访问。只能访问队头和队尾的元素,且只能进行入队和出队操作。
- 适用性:queue通常用于一些需要先进先出操作的场景,如任务调度、事件处理、缓冲区管理等。通过提供入队、出队、访问队头和队尾的操作,queue提供了一种方便且高效的方式来管理数据。
总体上,queue是一种功能简单而强大的容器适配器,可用于实现先进先出的数据结构。通过提供入队、出队、访问队头和队尾等操作,queue提供了一种方便且高效的方式来管理数据。
二. 模拟实现
我们的 stack 和 queue 都是适配器,所以我们实现就不用很麻烦,我们前面也写过 stack 和 queue,我们知道什么容器适合 stack 和 queue 那么我们这次就使用 vector 来适配 stack 使用 list 来适配 queue。
1. stack 模拟实现
这里我们模拟实现的逻辑就是复用,我们的 stack 的类里面有一个 vector 的对象,但是我们不一定使用 vector 我们还可以使用 list, 所以我们的 stack 的模板可以多传一个参数,来表示我们适配的容器时使用什么容器,然后我们可以复用我们容器的函数,例如我们的 stack 里面有一个 push 但是我们的 stack 只有一个入口和出口,还是后进先出的,所以我们使用的容器是 vector 的话,我们就可以使用 push_back 和 pop_back。
下面我们也就不一个一个介绍了,我们直接看代码~
template<class T, class Container = vector<T>>class stack{public:stack() {};~stack() {};void push(const T& val){_con.push_back(val);}void pop(){_con.pop_back();}size_t size(){return _con.size();}T& top(){return _con.back();}bool empty(){return _con.empty();}private:Container _con;};
2. queue 模拟实现
queue 和 stack 其实基本都是一样的,但是我们的 queue 的容器就不能使用 vector 了,其实并不是不能使用,而是使用 vector 的效率很低,因为我们的 vector 需要头删、尾插, 我们知道我们的 vector 的中间或者头部的插入删除都是很慢的,所以我们的 queue 可以使用 list ,我们下main还是直接看代码。
template<class T, class Container = list<T>>class queue{public:queue() {};~queue() {};void push(const T& val){_con.push_back(val);}void pop(){_con.pop_front();}size_t size(){return _con.size();}T& front(){return _con.front();}T& back(){return _con.back();}bool empty(){return _con.empty();}private:Container _con;};
模拟实现就完了~
三. deque
deque 是一个既可以随机插入删除,也可以支持随机访问的一个容器,但是我们经常宾补怎么使用它,我们的 deque 被作为 stack 和 queue 的默认容器,可是为什么选择 deque 呢?我们下面简单的了解一下 deque。
1. deque 接口
其他的接口我们就不看了,我们主要看一下这些接口
我们看到上面,我们的 deque 即提供了头插,头删,还有我们的 operator[],说明我们的 deque 的头插和头删的效率并不慢,而且还提供了我们的 operator[],说明我们的 deque 还支持随机访问,所以说它就是很适合做 stack 和 queue 的容器。
2. 底层
我们这里说一下它的底层,我们的 deque 的底层是一个一个连续的空间,而我们还有一个数组就是专门管理这些连续的空间,就像下面这样
然后我们头插就向最前面的那个数组插入,尾插就像后面的插入,如果满了,就扩容,到管理的那个数组里面在加一个指针,指向一块新的数组,总的来说 deque 是很复杂的,这么两句也说不清楚,但是 deque 其实并不是很重要,我们不需要掌握它,所以了解一下即可
源码在 码云 里面。