一、栈的应用
括号匹配问题
#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;
stack<int> S;
char str[110]; // 保存输入字符串
char ans[110]; // 保存输出字符串
int main() {
while (scanf("%s", str) != EOF) {
int i;
for (i = 0; str[i] != '\0'; i++) { // 从左到右遍历字符串
if (str[i] == '(') { // 若遇到左括号
S.push(i); // 将其数组下标放入栈中
ans[i] = ' '; // 暂且将对应的输出字符串位置改为空格
}
else if (str[i] == ')') { // 若遇到右括号
if (!S.empty()) { // 若此时栈非空
S.pop();
ans[i] = ' '; // 正确匹配,修改输出中该位置为空格
}
else ans[i] = '?'; // 若栈为空,则无法找到左括号与其匹配,修改输出中该位置为?
}
else ans[i] = ' '; // 若为其它字符,与括号匹配无关,则修改输出为空格
}
while (!S.empty()) { // 当字符串遍历完成后,尚留在栈中的左括号无法匹配
ans[S.top()] = '$'; // 修改骑在输出中的位置为$
S.pop(); // 弹出
}
ans[i] = '\0'; // 为了使输出形成字符串,在最后一个字符后添加一个空字符
printf("%s\n", str);
printf("%s\n", ans); // 输出原字符串和答案字符串
}
return 0;
}
当右括号出现时,和栈中左括号匹配时,要注意判断栈是否为空,不能直接s.top()
。
在牛客上打一个很简单的括号匹配问题时,我就是犯了这个错误,导致出现了段错误。
利用堆栈对表达式求值
#include <stack>
#include <iostream>
using namespace std;
char str[220]; // 保存表达式字符串
/*
优先级矩阵,若mat[i][j] == 1,则表示i号运算度优先级大于j号运算符
运算符编码规则为+为1号,-为2号,*为3号,/为4号,我们人为添加在表达式首尾的标记运算符为0号
*/
int mat[][5] = {1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0 };
stack<int> op; // 运算符栈,保存着运算符编号
stack<double> in; // 数字栈,运算结果可能存在浮点数,所以保存元素为double
/* 获得表达式中下一个元素函数,若函数运行结束时,引用变量reto为true,则表示该元素为一个运算符,其编号保存在引用变量retn中;
否则,表示该元素为一个数字,其值保存在引用变量retn中,引用变量i表示便利到的字符串下标
*/
void getOp(bool &reto, int & retn, int &i) {
if (i == 0 && op.empty() == true) { // 若此时遍历字符串第一个字符,且运算符栈为空,我们人为添加编号为0的标记字符
reto = true; // 为运算符
retn = 0; // 编号为0
return; // 返回
}
if (str[i] == '\0') { // 若此时遍历字符为空字符,则表示字符串已经被遍历完
reto = true; // 返回为运算符
retn = 0; // 编号为0的标记字符
return;
}
if (str[i] >= '0' && str[i] <= '9') { // 若当前字符为数字
reto = false;
} else { // 否则
reto = true;
if (str[i] == '+') {
retn = 1;
} else if (str[i] == '-') {
retn = 2;
} else if (str[i] == '*') {
retn = 3;
} else if (str[i] == '/') {
retn = 4;
}
i += 2; // i递增,跳过该运算字符和该运算字符后的空格
return;
}
retn = 0; // 返回结果为数字
// 若字符串未被遍历完,且下一个字符不是空格,则依次遍历其后数字,计算当前连续数字字符表示的数值
for (; str[i] != ' ' && str[i] != '\0'; i++) {
retn *= 10;
retn += str[i]-'0';
}
if (str[i] == ' ') // 若其后字符为空格,则表示字符串未被遍历完
i++; // 递增,跳过该空格
return;
}
int main() {
while (gets(str) ) { // 输入字符串,当其位于文件尾时,gets返回0
if (str[0] == '0' && str[1] == '\0') break; // 若输入只有一个0,则退出
bool retop;
int retnum; // 定义函数所需的引用变量
int idx = 0; // 定义遍历到的字符串下标,初始值为0
while (!op.empty())
op.pop();
while (!in.empty())
in.pop(); // 清空数字栈和运算符栈
while (true) { // 循环遍历表达式字符串
getOp(retop, retnum, idx); // 获取表达式中下一个元素
if (retop == false) { // 若该元素为数字
in.push((double)retnum); // 将其压人数字栈中
} else {
double tmp;
if (op.empty() || mat[retnum][op.top()] == 1) {
op.push(retnum);
} // 若运算符栈为空或者当前便利到的运算符优先级大于站定运算符,则将该运算符压入运算符栈
else {
while (mat[retnum][op.top()] == 0) { // 只要当前运算符优先级小于栈顶元素运算符,则重复循环
int ret = op.top(); // 保存栈顶运算符
op.pop();
double b = in.top();
in.pop();
double a = in.top();
in.pop();
if(ret == 1) tmp = a + b;
else if (ret == 2) tmp = a - b;
else if (ret == 3) tmp = a * b;
else tmp = a / b;
in.push(tmp); // 将结果压回数字栈
}
op.push(retnum); // 将当前运算符压入运算符栈
}
}
if (op.size() == 2 && op.top() == 0) break; // 若运算符栈中只有两个元素,且其栈顶元素为标记运算符明则表示表达式求值结束
}
printf("%.2f\n", in.top()); // 输出数字栈中位移的数字,即为答案
}
return 0;
}
这道题要注意,对栈中两个数字进行表达式求值时,在栈中下面位置的是左操作数,在上面的是右操作数。