栈
顺序栈的定义
int stk[N], top = -1;
具体操作见PPT。
链栈
1. 节点定义
链栈的节点通常定义如下:
struct Node
{
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
其中:
val
字段保存节点存储的数据next
字段指向下一个节点,实现链式存储结构
链栈的基本操作需要使用链表的基本操作,如:
- 初始化一个链栈常用头结点法,建立一个头结点,它的next指针初始化为NULL
- 插入节点时,新建一个节点,将它的next指向原来栈顶节点,然后将栈顶指针指向新节点
- 删除节点时,将栈顶指针指向原栈顶的next节点,释放原栈顶节点空间
- 访问栈顶元素直接通过栈顶指针访问其val字段
- 判断是否为空使用栈顶指针是否为NULL等
总之,通过next指针链接节点,实现链式存储,同时使用栈顶指针指向实际栈顶节点,就可以实现链栈的基本操作。
2. 初始化
链栈的初始化主要有两种方式:
- 初始化一个头结点
Node *init()
{
Node *dummy = new Node(-1);
return dummy;
}
此时头结点仅代表栈的头,不存储实际数据,next指针初始化为NULL表示栈为空。
- 不使用头结点
Node *init()
{
return NULL;
}
此时为空栈直接返回NULL。
使用头结点的优点是:
- 代码实现更简单,操作时直接使用 head指针代表栈 Head
- 判断栈空时用head->next判断,避免每次都对NULL指针进行判断
不使用头结点可以减少一个额外的结点开销。
一般来说,使用头结点法是较为常见的链栈初始化实现方式。它简化了空栈的判断及后续的插入、删除等操作。
所以使用头结点法初始化一个链栈一般是最佳实践。
接下来的写法都是基于带头节点的链表来实现。
3. 判栈空
bool empty(Node *dummy)
{
return dummy->next == NULL;
}
4. 进栈
void push(Node *dummy, int x)
{
Node *node = new Node(x);
node->next = dummy->next;
dummy->next = node;
}
5. 出栈
// 为了使代码简洁突出原理,以下所有操作默认都是合法的
void pop(Node *dummy)
{
dummy->next = dummy->next->next;
}
6. 读栈顶元素
int top(Node *dummy)
{
return dummy->next->val;
}
完整代码
#include <iostream>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
// 链栈初始化
Node *init()
{
Node *dummy = new Node(-1);
return dummy;
}
bool empty(Node *dummy)
{
return dummy->next == NULL;
}
void push(Node *dummy, int x)
{
Node *node = new Node(x);
node->next = dummy->next;
dummy->next = node;
}
// 为了使代码简洁突出原理,以下所有操作默认都是合法的
void pop(Node *dummy)
{
dummy->next = dummy->next->next;
}
int top(Node *dummy)
{
return dummy->next->val;
}
// 出栈链表剩余元素并输出
void print(Node *dummy)
{
while (!empty(dummy))
{
cout << top(dummy) << ' ';
pop(dummy);
}
cout << endl;
}
int main()
{
// 测试链栈各操作
Node *dummy = init();
push(dummy, 1);
push(dummy, 2);
push(dummy, 3);
push(dummy, 4);
printf("栈顶元素:%d\n", top(dummy));
pop(dummy);
printf("栈顶元素:%d\n", top(dummy));
printf("栈是否为空:%d\n", empty(dummy));
print(dummy);
return 0;
}
栈顶元素:4
栈顶元素:3
栈是否为空:0
3 2 1
class MyQueue {
private:
stack<int> in, out;
void in2out() {
while (!in.empty()) {
out.push(in.top());
in.pop();
}
}
public:
MyQueue() {}
void push(int x) {
in.push(x);
}
int pop() {
if (out.empty()) {
in2out();
}
int res = out.top();
out.pop();
return res;
}
int peek() {
if (out.empty()) {
in2out();
}
return out.top();
}
bool empty() {
return out.empty() && in.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
class MyStack {
private:
queue<int> q1, q2;
public:
MyStack() {}
void push(int x) {
q2.push(x);
while (!q1.empty()) {
q2.push(q1.front());
q1.pop();
}
swap(q1, q2);
}
int pop() {
int res = q1.front();
q1.pop();
return res;
}
int top() {
return q1.front();
}
bool empty() {
return q1.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
class MyStack {
private:
queue<int> q;
public:
MyStack() {}
void push(int x) {
int n = q.size();
q.push(x);
while (n --) {
q.push(q.front());
q.pop();
}
}
int pop() {
int res = q.front();
q.pop();
return res;
}
int top() {
return q.front();
}
bool empty() {
return q.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
括号匹配
这个代码利用STL的stack来实现:
- 遍历字符串,遇到开括号入栈
- 遇到闭括号出栈与之比对
- 最后判断栈是否为空,为空则括号匹配
通过给出具体代码,可以更清晰体现栈在括号匹配中的应用。
#include <iostream>
#include <stack>
using namespace std;
bool bracketMatch(string str)
{
stack<char> s;
for (char c: str)
{
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();
}
int main()
{
string s1 = "((()))";
string s2 = "([{{}])";
if(bracketMatch(s1))
cout << "s1 matched" << endl;
else
cout << "s1 not matched" << endl;
if(bracketMatch(s2))
cout << "s2 matched" << endl;
else
cout << "s2 not matched" << endl;
return 0;
}
AcWing.3302中缀表达式求值:
2种解法:
-
直接求解
-
将中缀表达式转换为后缀表达式后进行解决
完整代码链接:https://www.acwing.com/activity/content/code/content/1466076/
冲击高分的同学需要掌握具体代码的实现
冲击平均分的同学只需理解算法
这里给出第二种解法的参考代码:
#include <iostream>
#include <stack>
#include <string>
using namespace std;
// 操作符优先级
int priority(char op) {
if(op == '+' || op == '-')
return 1;
else if(op == '*' || op == '/')
return 2;
else
return 0;
}
string infixToPostfix(string infix) {
stack<char> st;
string postfix;
for(char c: infix) {
if(c == ' ') continue; //跳过空格
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { // c是字母
// if(isdigit(c)) { // c是具体的数字
postfix += c; //操作数直接加入后缀表达式
}
else if(c == '(') {
st.push(c); //左括号入栈
}
else if(c == ')') {
while(!st.empty() && st.top() != '(') {
postfix += st.top();
st.pop();
}
if(!st.empty() && st.top() != '(') {
return "Invalid Expression";
}
st.pop(); //弹出左括号
}
else { //运算符
while(!st.empty() && priority(c) <= priority(st.top())) {
postfix += st.top();
st.pop();
}
st.push(c);
}
}
while(!st.empty()) {
if(st.top() == '(')
return "Invalid Expression";
postfix += st.top();
st.pop();
}
return postfix;
}
int main() {
string infix;
cin >> infix;
string postfix = infixToPostfix(infix);
cout << postfix << endl;
return 0;
}
输入:
(a+b)*(c+d)
输出:
ab+cd+*