stack - 栈
std::stack 是基于后进先出 (LIFO) 原则的容器适配器,默认底层使用 deque 实现。在算法竞赛中,栈是实现深度优先搜索 (DFS) 和表达式求值的核心工具。
竞赛中的核心考点
| 操作 | 时间复杂度 | 竞赛要点 |
|---|
| 入栈 (push) | O(1) | 向栈顶添加元素 |
| 出栈 (pop) | O(1) | 从栈顶移除元素 |
| 访问栈顶 (top) | O(1) | 获取栈顶元素 |
| 判空 (empty) | O(1) | 检查栈是否为空 |
| 大小 (size) | O(1) | 获取栈大小 |
一、基本用法
1.1 初始化
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
stack<int> s;
stack<int, vector<int>> s_vec;
stack<int, deque<int>> s_deque;
stack<int, list<int>> s_list;
return 0;
}
1.2 基本操作
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
stack<int> s;
s.push(10);
s.push(20);
s.push(30);
cout << "栈顶: " << s.top() << endl;
s.pop();
cout << "出栈后栈顶: " << s.top() << endl;
cout << "栈大小: " << s.size() << endl;
cout << "栈是否为空: " << (s.empty() ? "是" : "否") << endl;
while (!s.empty())
{
s.pop();
}
cout << "清空后栈是否为空: " << (s.empty() ? "是" : "否") << endl;
return 0;
}
二、DFS 算法实现
2.1 基本 DFS
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
void dfs(int start, const vector<vector<int>>& adj)
{
int n = adj.size();
vector<bool> visited(n, false);
stack<int> s;
s.push(start);
visited[start] = true;
while (!s.empty())
{
int u = s.top();
s.pop();
cout << u << " ";
for (auto it = adj[u].rbegin(); it != adj[u].rend(); ++it)
{
int v = *it;
if (!visited[v])
{
visited[v] = true;
s.push(v);
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n = 6;
vector<vector<int>> adj(n);
adj[0] = {1, 2};
adj[1] = {0, 3, 4};
adj[2] = {0, 5};
adj[3] = {1};
adj[4] = {1};
adj[5] = {2};
cout << "DFS 遍历结果: ";
dfs(0, adj);
cout << endl;
return 0;
}
2.2 DFS 求路径
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
bool dfs_path(int start, int end, const vector<vector<int>>& adj, vector<bool>& visited, vector<int>& path)
{
visited[start] = true;
path.push_back(start);
if (start == end)
{
return true;
}
for (int v : adj[start])
{
if (!visited[v])
{
if (dfs_path(v, end, adj, visited, path))
{
return true;
}
}
}
path.pop_back();
return false;
}
vector<int> find_path(int start, int end, const vector<vector<int>>& adj)
{
vector<int> path;
vector<bool> visited(adj.size(), false);
dfs_path(start, end, adj, visited, path);
return path;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n = 6;
vector<vector<int>> adj(n);
adj[0] = {1, 2};
adj[1] = {0, 3, 4};
adj[2] = {0, 5};
adj[3] = {1};
adj[4] = {1};
adj[5] = {2};
vector<int> path = find_path(0, 5, adj);
cout << "DFS 路径: ";
for (int v : path)
{
cout << v << " ";
}
cout << endl;
return 0;
}
三、竞赛实战应用
3.1 表达式求值
int evaluate_postfix(const vector<string>& tokens)
{
stack<int> s;
for (const string& token : tokens)
{
if (token == "+" || token == "-" || token == "*" || token == "/")
{
int b = s.top();
s.pop();
int a = s.top();
s.pop();
if (token == "+") s.push(a + b);
else if (token == "-") s.push(a - b);
else if (token == "*") s.push(a * b);
else if (token == "/") s.push(a / b);
}
else
{
s.push(stoi(token));
}
}
return s.top();
}
vector<string> infix_to_postfix(const string& expr)
{
vector<string> postfix;
stack<char> op_stack;
unordered_map<char, int> precedence = {{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}, {'^', 3}};
for (int i = 0; i < expr.size(); i++)
{
char c = expr[i];
if (isdigit(c))
{
string num;
while (i < expr.size() && isdigit(expr[i]))
{
num += expr[i++];
}
i--;
postfix.push_back(num);
}
else if (c == '(')
{
op_stack.push(c);
}
else if (c == ')')
{
while (!op_stack.empty() && op_stack.top() != '(')
{
postfix.push_back(string(1, op_stack.top()));
op_stack.pop();
}
op_stack.pop();
}
else if (precedence.count(c))
{
while (!op_stack.empty() && precedence.count(op_stack.top()) &&
precedence[op_stack.top()] >= precedence[c])
{
postfix.push_back(string(1, op_stack.top()));
op_stack.pop();
}
op_stack.push(c);
}
}
while (!op_stack.empty())
{
postfix.push_back(string(1, op_stack.top()));
op_stack.pop();
}
return postfix;
}
3.2 括号匹配
bool is_valid_parentheses(const string& s)
{
stack<char> stk;
unordered_map<char, char> mapping = {{')', '('}, {']', '['}, {'}', '{'}};
for (char c : s)
{
if (mapping.count(c))
{
char top_element = stk.empty() ? '#' : stk.top();
stk.pop();
if (top_element != mapping[c])
{
return false;
}
}
else
{
stk.push(c);
}
}
return stk.empty();
}
3.3 单调栈
vector<int> daily_temperatures(vector<int>& temperatures)
{
int n = temperatures.size();
vector<int> result(n, 0);
stack<int> stk;
for (int i = n - 1; i >= 0; i--)
{
while (!stk.empty() && temperatures[stk.top()] <= temperatures[i])
{
stk.pop();
}
result[i] = stk.empty() ? 0 : stk.top() - i;
stk.push(i);
}
return result;
}
vector<int> next_greater_element(vector<int>& nums)
{
int n = nums.size();
vector<int> result(n, -1);
stack<int> stk;
for (int i = n - 1; i >= 0; i--)
{
while (!stk.empty() && stk.top() <= nums[i])
{
stk.pop();
}
result[i] = stk.empty() ? -1 : stk.top();
stk.push(nums[i]);
}
return result;
}
3.4 二叉树遍历
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
vector<int> preorder_traversal(TreeNode* root)
{
vector<int> result;
if (!root)
{
return result;
}
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty())
{
TreeNode* node = stk.top();
stk.pop();
result.push_back(node->val);
if (node->right)
{
stk.push(node->right);
}
if (node->left)
{
stk.push(node->left);
}
}
return result;
}
vector<int> inorder_traversal(TreeNode* root)
{
vector<int> result;
stack<TreeNode*> stk;
TreeNode* current = root;
while (current || !stk.empty())
{
while (current)
{
stk.push(current);
current = current->left;
}
current = stk.top();
stk.pop();
result.push_back(current->val);
current = current->right;
}
return result;
}
vector<int> postorder_traversal(TreeNode* root)
{
vector<int> result;
if (!root)
{
return result;
}
stack<TreeNode*> stk1, stk2;
stk1.push(root);
while (!stk1.empty())
{
TreeNode* node = stk1.top();
stk1.pop();
stk2.push(node);
if (node->left)
{
stk1.push(node->left);
}
if (node->right)
{
stk1.push(node->right);
}
}
while (!stk2.empty())
{
result.push_back(stk2.top()->val);
stk2.pop();
}
return result;
}
四、C++11 现代特性
4.1 使用 pair 元素访问
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
stack<pair<int, string>> stk;
stk.emplace(1, "apple");
stk.emplace(2, "banana");
while (!stk.empty())
{
pair<int, string> top = stk.top();
int id = top.first;
string name = top.second;
cout << "id: " << id << ", name: " << name << endl;
stk.pop();
}
return 0;
}
4.2 使用列表初始化
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
deque<int> d = {1, 2, 3, 4, 5};
stack<int> stk(d);
while (!stk.empty())
{
cout << stk.top() << " ";
stk.pop();
}
return 0;
}
五、性能优化
5.1 预分配空间
stack<int, vector<int>> stk;
vector<int> vec;
vec.reserve(100000);
stack<int, vector<int>> stk2(vec);
stack<int> stk3;
deque<int> dq;
dq.reserve(100000);
stack<int> stk4(dq);
5.2 避免不必要的拷贝
void process_stack(stack<int> stk)
{
}
void process_stack(stack<int>& stk)
{
}
stack<int> create_stack()
{
stack<int> stk;
return stk;
}
5.3 合理选择底层容器
| 底层容器 | 优点 | 缺点 | 适用场景 |
|---|
| deque(默认) | 两端操作高效 | 内存碎片 | 一般使用 |
| vector | 缓存友好 | 扩容开销 | 大小稳定 |
| list | 内存分配灵活 | 缓存不友好 | 频繁插入删除 |
六、常见错误与注意事项
| 错误 | 说明 | 解决方案 |
|---|
| 访问空栈 | 未定义行为 | 先检查 empty() |
| DFS 中栈溢出 | 递归深度过大 | 使用非递归 DFS |
| 单调栈逻辑错误 | 顺序处理不当 | 仔细检查入栈出栈条件 |
| 表达式求值错误 | 运算符优先级处理错误 | 正确实现中缀转后缀 |
| 内存使用过高 | 栈存储过多元素 | 考虑剪枝或优化算法 |
七、C++11 完整竞赛模板
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
void dfs(int start, const vector<vector<int>>& adj)
{
int n = adj.size();
vector<bool> visited(n, false);
stack<int> s;
s.push(start);
visited[start] = true;
while (!s.empty())
{
int u = s.top();
s.pop();
cout << u << " ";
for (auto it = adj[u].rbegin(); it != adj[u].rend(); ++it)
{
int v = *it;
if (!visited[v])
{
visited[v] = true;
s.push(v);
}
}
}
cout << endl;
}
bool is_valid_parentheses(const string& s)
{
stack<char> stk;
unordered_map<char, char> mapping = {{')', '('}, {']', '['}, {'}', '{'}};
for (char c : s)
{
if (mapping.count(c))
{
char top_element = stk.empty() ? '#' : stk.top();
stk.pop();
if (top_element != mapping[c])
{
return false;
}
}
else
{
stk.push(c);
}
}
return stk.empty();
}
vector<int> daily_temperatures(vector<int>& temperatures)
{
int n = temperatures.size();
vector<int> result(n, 0);
stack<int> stk;
for (int i = n - 1; i >= 0; i--)
{
while (!stk.empty() && temperatures[stk.top()] <= temperatures[i])
{
stk.pop();
}
result[i] = stk.empty() ? 0 : stk.top() - i;
stk.push(i);
}
return result;
}
int evaluate_postfix(const vector<string>& tokens)
{
stack<int> s;
for (const string& token : tokens)
{
if (token == "+" || token == "-" || token == "*" || token == "/")
{
int b = s.top();
s.pop();
int a = s.top();
s.pop();
if (token == "+" || token == "-") s.push(token == "+" ? a + b : a - b);
else s.push(token == "*" ? a * b : a / b);
}
else
{
s.push(stoi(token));
}
}
return s.top();
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n = 6;
vector<vector<int>> adj(n);
adj[0] = {1, 2};
adj[1] = {0, 3, 4};
adj[2] = {0, 5};
adj[3] = {1};
adj[4] = {1};
adj[5] = {2};
cout << "DFS 遍历: ";
dfs(0, adj);
string s = "{[]()}";
cout << "括号匹配: " << (is_valid_parentheses(s) ? "有效" : "无效") << endl;
vector<int> temperatures = {73, 74, 75, 71, 69, 72, 76, 73};
vector<int> result = daily_temperatures(temperatures);
cout << "每日温度: ";
for (int x : result)
{
cout << x << " ";
}
cout << endl;
vector<string> tokens = {"2", "1", "+", "3", "*"};
cout << "表达式求值: " << evaluate_postfix(tokens) << endl;
return 0;
}
总结:竞赛要点
- LIFO 特性:栈的后进先出特性是 DFS 的基础
- DFS 算法:栈是实现非递归 DFS 的标准工具
- 表达式求值:栈用于处理中缀、后缀表达式
- 括号匹配:栈是解决括号匹配问题的最佳选择
- 单调栈:用于解决下一个更大元素、每日温度等问题
- 二叉树遍历:栈用于实现非递归的树遍历
- 性能优化:选择合适的底层容器,避免不必要的拷贝
- 现代 C++11:使用 auto 类型推导、范围 for 循环和 emplace 原地构造
- 边界处理:注意栈判空,避免访问空栈
- 实战应用:图遍历、表达式处理、单调栈问题等
stack 是算法竞赛中解决后进先出问题的核心工具,掌握它的使用技巧能让你在处理深度优先搜索和表达式求值等问题时更加得心应手。