目录
[1.1 Stack的介绍](#1.1 Stack的介绍)
[1.2 Stack的使用](#1.2 Stack的使用)
[1.3 Stack的模拟实现](#1.3 Stack的模拟实现)
[2.1 Queue的介绍](#2.1 Queue的介绍)
[2.2 Queue的使用](#2.2 Queue的使用)
[2.3 Queue的模拟实现](#2.3 Queue的模拟实现)
[3.1 什么是适配器](#3.1 什么是适配器)
[3.2 为什么选择deque作为stack和queue的底层默认容器](#3.2 为什么选择deque作为stack和queue的底层默认容器)
专栏:C++学习笔记
在C++中,stack
和queue
是两种非常重要的数据结构,广泛应用于各种算法和系统中。本文将详细介绍这两种数据结构的基本概念、使用方法及其底层实现,并结合代码示例和运行结果进行详细讲解。
一、Stack(栈)
1.1 Stack的介绍
stack
(栈)是一种容器适配器,专门用于后进先出(LIFO, Last In First Out)的操作环境中。栈的元素插入和删除操作只能在容器的一端进行,即栈顶。
栈的底层容器可以是任何标准的容器类模板或一些其他特定的容器类,这些容器类应支持以下操作:
empty()
: 判空操作back()
: 获取尾部元素操作push_back()
: 尾部插入元素操作pop_back()
: 尾部删除元素操作
标准容器vector
、deque
、list
均符合这些需求,默认情况下,如果没有为stack
指定特定的底层容器,默认情况下使用deque
。
小李的理解:
栈就像是一叠盘子,你只能从顶部添加或移除盘子。这就意味着最后添加的盘子最先被移除(后进先出)。在C++中,栈的底层可以用多种容器实现,但一般默认用deque,因为它支持高效的尾部操作。
1.2 Stack的使用
stack
的常用操作包括:
stack()
: 构造空的栈empty()
: 检测stack是否为空size()
: 返回stack中元素的个数top()
: 返回栈顶元素的引用push(val)
: 将元素val
压入stack中pop()
: 将stack中尾部的元素弹出
示例代码:
#include <stack>
#include <iostream>
int main() {
std::stack<int> s;
s.push(1);
s.push(2);
s.push(3);
std::cout << "Stack top: " << s.top() << std::endl; // 输出3
s.pop();
std::cout << "Stack top after pop: " << s.top() << std::endl; // 输出2
return 0;
}
首先我们压入了三个元素1, 2, 3,栈顶元素是3。然后我们弹出了栈顶元素,栈顶变成了2。
小李的理解:
就像把三个盘子按顺序叠起来(1在最底下,3在最上面)。当我们移走最上面的盘子时,下面的盘子就成了新的顶部。
1.3 Stack的模拟实现
从栈的接口中可以看出,栈实际是一种特殊的vector
,因此使用vector
完全可以模拟实现stack
。
示例代码:
#include <vector>
#include <iostream>
namespace bite {
template<class T>
class stack {
public:
stack() {}
void push(const T& x) { _c.push_back(x); }
void pop() { _c.pop_back(); }
T& top() { return _c.back(); }
const T& top() const { return _c.back(); }
size_t size() const { return _c.size(); }
bool empty() const { return _c.empty(); }
private:
std::vector<T> _c;
};
}
int main() {
bite::stack<int> s;
s.push(1);
s.push(2);
s.push(3);
std::cout << "Custom stack top: " << s.top() << std::endl; // 输出3
s.pop();
std::cout << "Custom stack top after pop: " << s.top() << std::endl; // 输出2
return 0;
}
这表明我们的自定义stack
实现与标准库中的行为一致。
小李的理解:
我们自己实现了一个简单的栈,用vector来存储元素。每次添加元素时,将它们推到vector的尾部;每次移除元素时,从vector的尾部移除。这和我们平时用的栈行为完全一样。
二、Queue(队列)
2.1 Queue的介绍
queue
(队列)是一种容器适配器,专门用于先进先出(FIFO, First In First Out)的操作环境中。队列的元素插入操作在容器的一端进行,即队尾,而提取操作在容器的另一端进行,即队头。
队列的底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。这些容器类应支持以下操作:
empty()
: 检测队列是否为空size()
: 返回队列中有效元素的个数front()
: 返回队头元素的引用back()
: 返回队尾元素的引用push_back()
: 在队列尾部插入元素pop_front()
: 在队列头部删除元素
标准容器deque
和list
满足这些要求。默认情况下,如果没有为queue
指定特定的底层容器,则使用deque
。
小李的理解:
队列就像排队买票,最早来的人最先买到票(先进先出)。在C++中,队列的底层可以用多种容器实现,但一般默认用deque,因为它支持高效的头尾操作。
2.2 Queue的使用
queue
的常用操作包括:
queue()
: 构造空的队列empty()
: 检测队列是否为空size()
: 返回队列中有效元素的个数front()
: 返回队头元素的引用back()
: 返回队尾元素的引用push(val)
: 在队尾将元素val
入队列pop()
: 将队头元素出队列
示例代码:
#include <queue>
#include <iostream>
int main() {
std::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
std::cout << "Queue front: " << q.front() << std::endl; // 输出1
q.pop();
std::cout << "Queue front after pop: " << q.front() << std::endl; // 输出2
return 0;
}
首先我们压入了三个元素1, 2, 3,队头元素是1。然后我们弹出了队头元素,队头变成了2。
小李的理解:
就像排队买票,第一个人买了票离开,第二个人就成了最前面的人。
2.3 Queue的模拟实现
因为queue
的接口中存在头删和尾插,因此使用vector
来封装效率太低,故可以借助list
来模拟实现queue
。
示例代码:
#include <list>
#include <iostream>
namespace bite {
template<class T>
class queue {
public:
queue() {}
void push(const T& x) { _c.push_back(x); }
void pop() { _c.pop_front(); }
T& back() { return _c.back(); }
const T& back() const { return _c.back(); }
T& front() { return _c.front(); }
const T& front() const { return _c.front(); }
size_t size() const { return _c.size(); }
bool empty() const { return _c.empty(); }
private:
std::list<T> _c;
};
}
int main() {
bite::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
std::cout << "Custom queue front: " << q.front() << std::endl; // 输出1
q.pop();
std::cout << "Custom queue front after pop: " << q.front() << std::endl; // 输出2
return 0;
}
这表明我们的自定义queue
实现与标准库中的行为一致。
小李的理解:
我们自己实现了一个简单的队列,用list来存储元素。每次添加元素时,将它们推到list的尾部;每次移除元素时,从list的头部移除。这和我们平时用的队列行为完全一样。
三、容器适配器
3.1 什么是适配器
适配器是一种设计模式,该模式是将一个类的接口转换成客户希望的另外一个接口。在STL中,stack
和queue
就是通过适配器模式将deque
、vector
等容器类的接口转换成特定的LIFO或FIFO操作。
3.2 为什么选择deque作为stack和queue的底层默认容器
虽然stack
和queue
中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack
和queue
只是对其他容器的接口进行了包装,STL中stack
和queue
默认使用deque
。主要原因如下:
stack
和queue
不需要遍历(因此stack
和queue
没有迭代器),只需要在固定的一端或者两端进行操作。- 在
stack
中元素增长时,deque
比vector
的效率高(扩容时不需要搬移大量数据);queue
中的元素增长时,deque
不仅效率高,而且内存使用率高。结合了
deque
的优点,而完美地避开了其缺陷,使其成为stack
和queue
的理想底层容器。
总结
C++中的stack
和queue
通过容器适配器实现,分别用于LIFO和FIFO操作。stack
和queue
的底层容器默认使用deque
,但也可以根据需求选择其他标准容器。理解并灵活运用这些数据结构,对于高效编写算法和处理复杂数据具有重要意义。