自学内容网 自学内容网

数据结构 -- # 栈的应用 表达式求值 括号匹配,波兰表达式和逆波兰表达式的详解(C++)

若没有相关知识基础,可以先看看下面文章哦🤗👇
栈和队列基本概念


括号匹配

给定若干个括号,如
(((())))

发现最后出现的左括号最先被右括号匹配

那么我们可以利用栈的特性,如果遇到左括号我们就压入栈中,

如果遇到右括号,那么我们就将当前栈顶元素弹出,看是否可以匹配

匹配流程
① : 出栈匹配的过程中,如果此时栈空那么匹配失败
② : 出栈匹配的过程中,如果不相匹配那么匹配失败
③ : 如果序列扫描完毕,最后栈不为空,说明还有剩余的左括号,匹配失败

表达式求值

中缀表达式 : 运算符在中间,是我们平常使用的类型=> a + b
(逆波兰表达式)后缀表达式 : 运算符在后面 a b +
(波兰表达式)前缀表达式 : 运算符在前面 +a b

中缀转后缀手算

  1. 确定中缀表达式中各个运算符的运算顺序
  2. 以[左操作数 右操作数 运算符]的方式组合成一个新的操作数
    如 a+b 变为 ab+ ,我们要将ab+作为一个新的操作数去用

需要知道的是,如果存在a+b+c+d这种样子的中缀表达式,
我们的运算可以先a+b,也可以是b+c,这样会产生不同的后缀表达式

中缀表达式的运算顺序不唯一,会产生不同的后缀表达式

为了方便机器的运算,我们采用**左优先**原则

只要左边的运算符能先计算,就优先计算左边的数据

后缀表达式的机算

从左往右扫描元素,扫描到操作数则压入栈
扫描到运算符则弹出两个栈顶元素,执行运算后再压入栈顶

若表达式合法,则最后栈中只会留下一个元素(最终结果)

中缀转前缀手算

使用右优先原则

按照[运算符 左操作数 右操作数]的方式组合新的操作数

前缀表达式的机算

从右往左扫描元素,其它操作仿照后缀表达式的计算即可

中缀转后缀的机算

栈的作用是保存不能确定运算顺序的运算符

  1. 遇到操作数,直接加入后缀表达式
  2. 遇到界限符,如果是左括号那么入栈,如果是右括号则依次弹出栈内运算符并加入到后缀表达式,直到碰到右括号为止
    因为括号内的运算符一定是先生效的
  3. 遇到运算符,依次弹出栈中优先级高于等于当前运算符的所有运算符
    等于也要弹出是因为要遵守左优先原则
    优先级高的要弹出是因为其要先生效

中缀表达式的代码实现

如何编程计算一个中缀表达式?

需要实现中缀转为后缀,然后机算后缀表达式两个算法

  1. 初始化操作数栈,用来存放操作数
  2. 初始化运算符栈,用来存储运算符
  3. 扫描序列
    • 如果扫描到操作数,则压入操作数栈
    • 如果扫描到运算符或者界限符,则压入运算符栈
  4. 按照中缀转后缀的逻辑,每弹出一个运算符,就要弹出两个操作数进行相应的运算,然后再把运算结果压入操作数栈
  5. 最后操作数栈中留下的元素就为答案

完整代码

//这里是引入了一些常用的头文件,和一些常规操作
//第一行是因为该死的编译器不让直接用scanf
#define _CRT_SECURE_NO_WARNINGS 1
#define _USE_MATH_DEFINES //启用M_PI的定义
#include<algorithm>
#include<iostream>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#define MAX 0x3f3f3f3f
#define MIN -0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;

stack<int> num;//操作数栈
stack<char> op;//运算符栈

//定义符号的优先级
map<char, int> m{ {'+',1},{'-',1},{'*',2},{'/',2} };

//计算后缀表达式
void eval() {
//注意栈顶元素是第二个操作数
int x2 = num.top(); num.pop();
int x1 = num.top(); num.pop();
char p = op.top(); op.pop();

int res = 0;
if (p == '+')res = x1 + x2;
else if (p == '-')res = x1 - x2;
else if (p == '*')res = x1 * x2;
else res = x1 / x2;

num.push(res);
}

int main() {
string s;
cin >> s;
//中缀转后缀
for (int i = 0; i < s.size(); i++) {
//如果当前是数字直接加入操作数栈
//当然我们得先把它变成数字
if (isdigit(s[i])) {
int x = 0, j = i;
while (j < s.size() && isdigit(s[j])) {
x = x * 10 + s[j] - '0';
j++;
}
num.push(x);
//回退i到j-1的位置,i然后自增
i = j - 1;
}
//左括号直接入运算符栈
else if (s[i] == '(')op.push(s[i]);
//如果是右括号,那么我们就可以依次弹出碰到左括号
//内的所有运算符,弹出一个运算符就需要弹出两个操作数
//进行运算
else if (s[i] == ')') {
while (op.top() != '(')eval();
op.pop();//弹出左括号
}
//如果是运算符,那么考虑优先级
//如果栈顶的运算符优先级大于等于当前运算符的优先级
//那么弹出进行运算
else {
while (op.size() > 0 && m[op.top()] >= m[s[i]])eval();
op.push(s[i]);
}
}
//最后计算剩下的数据
while (op.size())eval();
cout << num.top() << endl;
return 0;
}


总结

根据栈的先进后出的特性,我们可以利用栈来进行括号匹配和表达式求值的问题


🌻编写本篇文章目的是笔者想以输出的形式进行学习,顺便记录学习点滴🌻

🌹 如果本篇文章对你有帮助的话那就点个赞吧👍🌹

😇 本篇文章存在多处不足,如有修改意见,可以私信或者评论哦,还望海涵 😇

🙇‍本篇文章参考于Acwing,王道数据结构



原文地址:https://blog.csdn.net/mzh1213/article/details/144323799

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!