《C++》stack容器详解
文章目录
- 一、栈的基本概念
- 二、stack的声明和初始化
- 三、stack的基本操作
- 3.1 元素压栈(push)
- 3.2 元素出栈(pop)
- 3.3 访问栈顶元素(top)
- 3.4 检查栈是否为空(empty)
- 3.5 获取栈的大小(size)
- 3.6 示例
- 四、stack的实现细节
- 自定义栈的实现
- 五、stack的实际应用
- 5.1 括号匹配
- 5.2 表达式求值
- 六、写在最后的话
栈(stack)是计算机科学中最基础也是最重要的数据结构之一,它遵循"后进先出"的原则。在C++标准库中,stack被实现为一个容器适配器,提供了简洁高效的栈操作接口。本文将全面介绍C++中stack的使用方法、特性以及实际应用场景
一、栈的基本概念
-
栈是一种限制访问端的线性数据结构,它只允许在一端(称为栈顶)进行插入和删除操作。这种特性使得栈在很多场景下非常有用,比如函数调用、表达式求值、括号匹配等。
-
栈的两个基本操作是:
push - 将元素压入栈顶pop - 从栈顶弹出元素
-
C++中的stack容器适配器实际上是基于其他序列容器(如deque或list)实现的,默认情况下使用deque作为底层容器。
二、stack的声明和初始化
- 在C++中使用stack需要包含头文件:
#include <stack>
#include <iostream>
using namespace std;
- 声明一个stack非常简单:
stack<int> myStack; // 创建一个存储int类型的空栈
- 我们也可以在声明时初始化stack:
stack<int> initStack({1, 2, 3, 4, 5}); // 使用初始化列表创建栈
三、stack的基本操作
3.1 元素压栈(push)
- 使用push()方法将元素添加到栈顶:
stack<int> s;
s.push(10); // 栈:10
s.push(20); // 栈:20 10
s.push(30); // 栈:30 20 10
3.2 元素出栈(pop)
- 使用pop()方法移除栈顶元素:
s.pop(); // 移除30,栈变为:20 10
注意:pop()函数不返回被移除的元素,只是移除它。如果需要访问栈顶元素,应该先使用top()。
示例:
#include <iostream>
#include <stack>
using namespace std;int main() {stack<int> s;// 向栈中添加元素s.push(10);s.push(20);s.push(30);// 现在栈的内容是(从顶到底): 30 -> 20 -> 10// 错误示范:试图直接获取pop()的返回值// int topElement = s.pop(); // 这样写是错误的!因为pop()不返回值// 正确做法:先使用top()获取栈顶元素,再pop()移除它int topElement = s.top(); // 获取栈顶元素30s.pop(); // 移除栈顶元素30cout << "取出的元素是: " << topElement << endl; // 输出: 取出的元素是: 30cout << "现在栈顶元素是: " << s.top() << endl; // 输出: 现在栈顶元素是: 20return 0;
}
3.3 访问栈顶元素(top)
- 使用top()方法访问栈顶元素:
cout << "栈顶元素是: " << s.top() << endl; // 输出20
3.4 检查栈是否为空(empty)
- empty()方法用于判断当前栈是否不包含任何元素,这是一个在实际编程中必须时刻注意的安全检查。
- 返回类型:
bool
(true表示栈为空,false表示栈不为空)
if (s.empty()) {cout << "栈为空" << endl;
} else {cout << "栈不为空" << endl;
}
3.5 获取栈的大小(size)
-
size()方法是C++ stack容器提供的一个基础但实用的功能,它返回当前栈中存储的元素数量。理解这个方法的特点和正确使用方式对于编写健壮的栈操作代码非常重要。
-
返回值类型:返回size_type(通常是无符号整型,如size_t)
-
空栈返回值:当栈为空时,返回0
cout << "栈的大小: " << s.size() << endl;
3.6 示例
// 定义一个可以装任何类型东西的栈类
// T 表示可以装int、string等各种类型
template <typename T>
class MyStack {
private:vector<T> data; // 用动态数组当容器来装东西public:// 往栈里放东西(像叠盘子)void push(const T& item) {data.push_back(item); // 把新东西放到数组最后面}// 从栈顶拿走东西(像拿最上面的盘子)void pop() {if (!empty()) { // 先看看栈是不是空的data.pop_back(); // 如果不是空的,就删掉最后一个}// 如果是空的就什么都不做}// 看看栈顶是什么东西(只看不拿)T& top() {return data.back(); // 返回数组最后一个元素}// 检查栈是不是空的bool empty() const {return data.empty(); // 数组空就是栈空}// 看看栈里有多少东西size_t size() const {return data.size(); // 返回数组里元素个数}
};
四、stack的实现细节
虽然我们通常不需要关心stack的底层实现,但了解一些细节有助于更好地使用它
-
容器适配器:stack不是一个独立的容器,而是基于其他容器(默认是deque)的适配器。这意味着它提供了特定的接口,但底层存储由其他容器处理。
-
模板参数:stack的完整声明实际上是:
template <class T, class Container = deque<T>> class stack;
这意味着我们可以指定底层容器类型,例如:
stack<int, vector<int>> vecStack; // 使用vector作为底层容器
- 性能保证:由于stack的操作都发生在栈顶,所有操作的时间复杂度都是O(1)。
自定义栈的实现
- 虽然标准库的stack足够好用,但了解其实现原理也很重要。下面是一个简单的stack实现:
template <typename T>
class MyStack {
private:vector<T> data;
public:void push(const T& item) {data.push_back(item);}void pop() {if (!empty()) {data.pop_back();}}T& top() {return data.back();}bool empty() const {return data.empty();}size_t size() const {return data.size();}
};
五、stack的实际应用
5.1 括号匹配
- stack非常适合解决括号匹配问题:
bool isBalanced(const string& expr) {stack<char> s;for (char c : expr) {if (c == '(' || c == '[' || c == '{') {s.push(c);} else {if (s.empty()) return false;char top = s.top();s.pop();if ((c == ')' && top != '(') || (c == ']' && top != '[') || (c == '}' && top != '{')) {return false;}}}return s.empty();
}
5.2 表达式求值
- stack可以用于中缀表达式转后缀表达式,以及后缀表达式的求值:
int evaluatePostfix(const string& exp) {stack<int> s;for (char c : exp) {if (isdigit(c)) {s.push(c - '0');} else {int val1 = s.top(); s.pop();int val2 = s.top(); s.pop();switch (c) {case '+': s.push(val2 + val1); break;case '-': s.push(val2 - val1); break;case '*': s.push(val2 * val1); break;case '/': s.push(val2 / val1); break;}}}return s.top();
}
六、写在最后的话
如果文中有任何错误或不足之处,恳请各位前辈和老师不吝指教。编程之路漫长,我会继续保持谦逊学习的态度,一步一个脚印地进步。也欢迎各位初学者朋友一起交流学习心得,共同成长。
----------------[全文完]----------------