C++ 中栈 (Stack) 详解和常见面试示例汇总实现
1. 栈的基本概念
-
定义:栈(Stack)是一种 先进后出(LIFO, Last In First Out) 的数据结构。
-
特点:
- 只能在一端(栈顶 Top)进行插入(Push)和删除(Pop)操作。
- 操作受限,逻辑简单。
-
常见操作:
push(x)
:将元素压入栈顶pop()
:弹出栈顶元素top()
/peek()
:获取栈顶元素(不删除)empty()
:判断栈是否为空size()
:获取栈中元素个数
2. 栈的底层实现
在 C++ 中,栈没有单独的底层结构,而是依赖 容器适配器 (Container Adapter) 实现。
-
常见底层容器:
deque
(默认):双端队列,支持快速的首尾插入删除。vector
:连续存储,适合仅栈顶操作的场景。list
:双向链表。
源码:
template <class T, class Container = deque<T>>
class stack {
protected:Container c; // 底层容器
public:bool empty() const { return c.empty(); }size_t size() const { return c.size(); }T& top() { return c.back(); }void push(const T& val) { c.push_back(val); }void pop() { c.pop_back(); }
};
这说明 stack
本质上是对底层容器的封装,限制了只能 后进先出。
3. C++ STL 中的 stack
用法
需要头文件:
#include <stack>
#include <iostream>
using namespace std;
常用接口
stack<int> s;// 压栈
s.push(10);
s.push(20);// 访问栈顶
cout << s.top() << endl; // 20// 弹栈
s.pop(); // 删除 20
cout << s.top() << endl; // 10// 判断是否为空
if (!s.empty()) cout << s.size() << endl; // 1
4. 栈的常见应用
(1) 括号匹配
bool isValid(string s) {stack<char> st;for (char c : s) {if (c == '(') st.push(')');else if (c == '[') st.push(']');else if (c == '{') st.push('}');else if (st.empty() || st.top() != c) return false;else st.pop();}return st.empty();
}
(2) 表达式求值(后缀表达式)
int evalRPN(vector<string>& tokens) {stack<int> st;for (string& t : tokens) {if (t == "+" || t == "-" || t == "*" || t == "/") {int b = st.top(); st.pop();int a = st.top(); st.pop();if (t == "+") st.push(a + b);else if (t == "-") st.push(a - b);else if (t == "*") st.push(a * b);else st.push(a / b);} else {st.push(stoi(t));}}return st.top();
}
(3) 单调栈(求最近更大/更小元素)
vector<int> nextGreaterElement(vector<int>& nums) {vector<int> res(nums.size(), -1);stack<int> st;for (int i = 0; i < nums.size(); i++) {while (!st.empty() && nums[st.top()] < nums[i]) {res[st.top()] = nums[i];st.pop();}st.push(i);}return res;
}
5. 自己实现一个栈
基于 动态数组:
template <typename T>
class MyStack {
private:vector<T> data;
public:void push(T val) { data.push_back(val); }void pop() { if (!empty()) data.pop_back(); }T top() { return data.back(); }bool empty() { return data.empty(); }size_t size() { return data.size(); }
};
基于 链表:
template <typename T>
class Node {
public:T val;Node* next;Node(T v) : val(v), next(nullptr) {}
};template <typename T>
class LinkedStack {
private:Node<T>* head = nullptr;
public:void push(T val) {Node<T>* node = new Node<T>(val);node->next = head;head = node;}void pop() {if (head) {Node<T>* tmp = head;head = head->next;delete tmp;}}T top() { return head->val; }bool empty() { return head == nullptr; }
};
6. 栈的优缺点
优点:
- 操作简单,效率高
- 应用广泛:括号匹配、递归、表达式求值
缺点:
- 功能有限,只能操作栈顶
- 容量有限制(系统调用栈有栈溢出风险)
7. 与其他容器对比
数据结构 | 插入删除位置 | 访问时间 | 适用场景 |
---|---|---|---|
栈 stack | 仅栈顶 | O(1) | LIFO 算法 |
队列 queue | 队尾入队,队首出队 | O(1) | FIFO 算法 |
双端队列 deque | 两端均可 | O(1) | 双端操作 |
向量 vector | 任意位置 | O(1) 随机访问 | 动态数组 |
8. 常见面试题-实现示例汇总
1. 括号匹配(LeetCode 20: Valid Parentheses)
思路
- 遍历字符串,遇到左括号压栈,遇到右括号检查是否匹配栈顶。
- 最后栈必须为空。
bool isValid(string s) {stack<char> st;for (char c : s) {if (c == '(') st.push(')');else if (c == '[') st.push(']');else if (c == '{') st.push('}');else if (st.empty() || st.top() != c) return false;else st.pop();}return st.empty();
}
2. 最小栈(LeetCode 155: Min Stack)
思路
- 用两个栈:一个正常存值,一个维护最小值。
class MinStack {stack<int> st, minSt;
public:void push(int val) {st.push(val);if (minSt.empty() || val <= minSt.top()) minSt.push(val);}void pop() {if (st.top() == minSt.top()) minSt.pop();st.pop();}int top() { return st.top(); }int getMin() { return minSt.top(); }
};
3. 用栈实现队列(LeetCode 232: Implement Queue using Stacks)
思路
- 用两个栈:一个负责入队,一个负责出队。
- 出队时如果出栈为空,就把入栈的元素全部倒过去。
class MyQueue {stack<int> in, out;
public:void push(int x) { in.push(x); }int pop() {peek();int x = out.top(); out.pop();return x;}int peek() {if (out.empty()) {while (!in.empty()) {out.push(in.top());in.pop();}}return out.top();}bool empty() { return in.empty() && out.empty(); }
};
4. 用队列实现栈(LeetCode 225: Implement Stack using Queues)
思路
- 用一个队列,每次 push 后把前面的元素重新入队到队尾,从而保持栈顶在队首。
class MyStack {queue<int> q;
public:void push(int x) {q.push(x);for (int i = 0; i < q.size() - 1; i++) {q.push(q.front());q.pop();}}int pop() {int x = q.front(); q.pop();return x;}int top() { return q.front(); }bool empty() { return q.empty(); }
};
5. 逆波兰表达式求值(LeetCode 150: Evaluate Reverse Polish Notation)
思路
- 遇到数字就压栈,遇到运算符就弹出两个数运算后再压栈。
int evalRPN(vector<string>& tokens) {stack<int> st;for (string& t : tokens) {if (t == "+" || t == "-" || t == "*" || t == "/") {int b = st.top(); st.pop();int a = st.top(); st.pop();if (t == "+") st.push(a + b);else if (t == "-") st.push(a - b);else if (t == "*") st.push(a * b);else st.push(a / b);} else {st.push(stoi(t));}}return st.top();
}
6. 柱状图最大矩形(LeetCode 84: Largest Rectangle in Histogram)
思路
- 单调栈维护递增高度索引,遇到下降时计算面积。
int largestRectangleArea(vector<int>& heights) {stack<int> st;heights.push_back(0); // 哨兵,保证清空栈int n = heights.size(), res = 0;for (int i = 0; i < n; i++) {while (!st.empty() && heights[st.top()] > heights[i]) {int h = heights[st.top()]; st.pop();int l = st.empty() ? -1 : st.top();res = max(res, h * (i - l - 1));}st.push(i);}return res;
}
7. 下一个更大元素(LeetCode 496: Next Greater Element I)
思路
- 单调栈,维护递减栈,遇到更大元素时更新映射。
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {unordered_map<int,int> mp;stack<int> st;for (int x : nums2) {while (!st.empty() && st.top() < x) {mp[st.top()] = x;st.pop();}st.push(x);}vector<int> res;for (int x : nums1) res.push_back(mp.count(x) ? mp[x] : -1);return res;
}
小结
这些题展示了 栈的典型应用场景:
- 括号匹配 → 符号配对
- 最小栈 → 辅助栈维护状态
- 栈 ↔ 队列 → 数据结构模拟
- 逆波兰表达式 → 表达式计算
- 柱状图最大矩形 → 单调栈区间计算
- 下一个更大元素 → 单调栈找最近元素