实验题目:
表达式求值问题。这里限定的表达式求值问题是: 用户输入一个包含“+”、“-”、“*”、“/”、正整数和圆括号的合法数学表达式,计算该表达式的运算结果。
算术表达式求值过程是: STEP 1:先将算术表达式转换成后缀表达式。STEP 2:然后对该后缀表达式求值。
算法伪代码:
#include<iostream>
#include<vector>
#include<string>
#include<stack>
using namespace std;
//判断运算符优先级
int Precede(char a, char b) //a 为栈顶运算符,b 为 exp[i]
{
string s1 = "=(+-*/)", s2 = "=(+-*/)";
int m = 0, n = 0, PriM[7][7] = { //运算符优先级矩阵,横向为栈顶元素,纵向为exp[i]
0,-1,-1,-1,-1,-1,-1,
1,-1,-1,-1,-1,-1,0,
1,-1,1,1,-1,-1,1,
1,-1,1,1,-1,-1,1,
1,-1,1,1,1,1,1,
1,-1,1,1,1,1,1,
1,0,1,1,1,1,1
};
while (s1[m] != a) //s1逐个和栈顶元素比较,找到栈顶元素在矩阵中所对应的行号
m++;
while (s2[n] != b) //s2逐个和 exp[i] 比较,找到 exp[i] 在矩阵中所对应的行列号
n++;
return PriM[m][n]; //输出行列号在矩阵中所对应的数值,即为栈顶元素和 exp[i] 优先级的比较结果
}
//判断是否输入的是正确的运算符
bool judgement(char ch)
{
if (ch == '(' || ch == ')' || ch == '+' || ch == '-'
|| ch == '*' || ch == '/')
return true;
else
return false;
}
//中缀表达式转后缀表达式
int transformation(string& exp1, string& postexp1)//参数采用引用
{
stack<char>opr;
opr.push('=');
int i = 0, j = 0;
while (exp1[i] != '\0')
{
if (exp1[i] >= '0' && exp1[i] <= '9')//若为数字字符,则将后续的所有数字均依次存放到postexp中,并以字符'#'标志数值串结束
{
while (exp1[i] >= '0' && exp1[i] <= '9')
postexp1[j++] = exp1[i++];
postexp1[j++] = '#';
}
else
{
if (judgement(exp1[i]) != 0)//判断是否输入的是正确的运算符
{
switch (Precede(opr.top(), exp1[i]))
{
case -1: //栈顶运算符优先级低,进栈
opr.push(exp1[i]);
i++;
break;
case 0: //只有栈顶为 '(' exp[i] 为 ')' 时满足这种情况
opr.pop(); //将 '(' 出栈
i++;
break;
case 1: //栈顶运算符优先级高,栈顶运算符出栈放入postexp中
postexp1[j++] = opr.top();
opr.pop();
break;
}
}
else {
cout << "ERROR!!!" << endl;
for (int k = 0; k < 100; k++) //清空postexp,全赋值为 '\0',否则错误运算符之前的有效字符会残留
postexp1[k] = '\0';
return -1; //给出输入运算符出错信息
break;
}
}
}
while (opr.top() != '=') //字符串exp扫描完毕,将运算符栈opr中'='之前的所有运算符依次出栈并存放到postexp中
{
postexp1[j++] = opr.top();
opr.pop();
}
return 0;
}
//后缀表达式求值
double calculate(string postexp)
{
stack<double>opd;
int i = 0, flag = 0;
double a = 0, b = 0, c, d, result;
while (postexp[i] != '\0')
{
if (!(postexp[i] >= '0' && postexp[i] <= '9')) //当 postexp[i] 是运算符时出栈两个元素
{
b = opd.top(); //返回栈顶元素的引用,不出栈栈顶元素
opd.pop(); //将栈顶元素出栈,因为 pop() 函数返回值为 void 型,所以不能 b=opd.pop()
a = opd.top();
opd.pop();
}
switch (postexp[i])
{
case '+':
c = a + b;
opd.push(c);
break;
case '-':
c = a - b;
opd.push(c);
break;
case '*':
c = a * b;
opd.push(c);
break;
case '/':
if (b == 0) { //除数为零两种情况,一是栈中恰有两个元素,取出栈中两个元素,不计算结果放入栈中,导致栈为空
flag = -1; //给出除数为零的信息
cout << "Divide by zero excepition!" << endl;//二是,栈中多于两个元素,取出栈中两个元素后,栈虽不为空,但返回值不是计算的正确结果
break;
}
else {
c = a / b;
opd.push(c);
break;
}
default:
d = 0;
while (postexp[i] >= '0' && postexp[i] <= '9') //循环将字符转换为正确的数字放入 opd 栈内
{
d = d * 10 + postexp[i] - '0';
i++; //当数字正确转换后,执行 i++ 此时 postexp[i] 为 '#'
}
opd.push(d); //转换正确的数字入栈
}
if (flag == -1)
break;
i++; //执行 i++ 后,此时 postexp[i] 已是下一个数字字符,所以 switch 不用考虑字符为 '#' 的情况
}
if (flag == -1) {
result = -1;
cout << "Calculation Failure!" << endl;
}
else
result = opd.top(); //返回栈顶元素,即为表达式的结果
return result;
}
int main()
{
string exp, postexp;
cout << "Please enter a positive integer value:" << endl;
cin >> exp;
for (int k = 0; k <100; k++) //由于postexp未初始化,而 string 类不允许采用单个字符赋值给 未初始化的 postexp 以 postexp[j++] 的形式,
postexp = postexp + '\0'; //所以,使用string类的字符串连接方式对 postexp 初始化,初始化的空间要足够大
while (transformation(exp, postexp) == -1)
{
cout << endl;
cout <<"Please enter the correct expression:" << endl;
cin >> exp;
}
cout << "The postexp is: " << postexp << endl;
cout << "The result is: " << calculate(postexp) << endl;
return 0;
}
upgrade
#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <cmath>
using namespace std;
// 中缀表达式:A + B * (C - D) - E / F
/*
"左优先"原则:只要左边的运算符能先计算,就优先算左边的
后缀表达式:
"左优先",使用,表达式中从左到右运算符出现的顺序和中缀表达式的运算符生效的顺序一致,适合计算机计算使用,计算效率高
A B C D - * + E F / -
不是"左优先"原则,计算机计算不使用
A B C D - * E F / - +
算法实现是从左向右扫描后缀表达式
*/
/*
"右优先"原则:只要右边的运算符能先计算,就优先算右边的
前缀表达式:
"右优先",使用,表达式中运算符从右到左出现的顺序和中缀表达式的运算符生效的顺序一致,适合计算机计算使用,计算效率高
+ A - * B - C D / E F
不是"左右先"原则,计算机计算不使用
- + A * B - C D / E F
算法实现是从右向左扫描前缀表达式
*/
// operator(运算符),operand(操作数)
// 判断运算符优先级
int precede(char a, char b) // a 为当前扫描到的运算符,b 为栈顶运算符
{
/*
1:当前扫描的运算符比栈顶运算符优先级高
0:当前扫描的运算符和栈顶运算符优先级相同
-1:当前扫描的运算符比栈顶运算符优先级低
s2 ( + - * / 栈顶
s1 + 1 0, 0, -1, -1,
- 1 0, 0, -1, -1,
* 1 1, 1, 0, 0,
/ 1 1, 1, 0, 0,
*/
string s1 = "+-*/", s2 = "(+-*/";
int m = 0, n = 0, PriM[4][5] = { // 运算符优先级矩阵
1, 0, 0, -1, -1,
1, 0, 0, -1, -1,
1, 1, 1, 0, 0,
1, 1, 1, 0, 0,
};
while (s1[m] != a) // 找到在s1中和当前扫描的运算符相等的运算符的序号,即为当前扫描的运算符在矩阵中的行号
m++;
while (s2[n] != b) // 找到在s2中和栈顶运算符相等的运算符的序号,即为栈顶运算符在矩阵中的列号
n++;
return PriM[m][n]; // 输出行列号在矩阵中所对应的数值,即为当前扫描的运算符和栈顶运算符优先级的比较结果
}
// 判断是否输入的是合法的表达式
bool judge_exp_legality(string exp) {
string::iterator it;
int flag = 1;
for (it = exp.begin(); it != exp.end(); ++it) {
if (*it != '(' && *it != ')' && *it != '+' && *it != '-' && *it != '*' && *it != '/' && *it < '0' || *it > '9') {
flag = 0;
break;
}
}
return flag == 1;
}
bool calculate(stack<char> &opr, stack<int> &opd) {
char ch; // 记录运算符栈出栈的运算符
ch = opr.top();
opr.pop();
int a, b; // a记录运算符前面的操作数,b记录运算符后面的操作数
b = opd.top();
opd.pop();
a = opd.top();
opd.pop();
int c = 0; // 记录每次出栈的两个操作数的运算结果
int flag; // 记录本次运算是否成功(即运算过程中没有出现除数为零的情况),flag = 1成功,flag = 0失败
switch (ch) {
case '+':
c = a + b;
opd.push(c);
break;
case '-':
c = a - b;
opd.push(c);
break;
case '*':
c = a * b;
opd.push(c);
break;
case '/':
if (b == 0) {
return false;
} else {
c = a / b;
opd.push(c);
break;
}
default:
break;
}
return true;
}
// 计算中缀表达式的值,将中缀表达式转后缀表达式算法和后缀表达式求值算法结合
int evaluate_infix_expression(string exp) {
stack<char> opr; // 运算符栈
stack<int> opd; // 操作数栈
int flag = 1; // 记录本次运算是否成功(即运算过程中没有出现除数为零的情况),flag = 1成功,flag = 0失败
stack<char> temp; // 将中缀表达式中的数字字符转换为整数
string::iterator it_exp;
for (it_exp = exp.begin(); it_exp != exp.end(); ++it_exp) {
if (*it_exp >= '0' && *it_exp <= '9') {
while (*it_exp >= '0' && *it_exp <= '9') {
temp.push(*it_exp);
it_exp++;
if (it_exp == exp.end())
break;
}
it_exp--; // 防止it_exp向后移动两位,直接跳过了一个字符
int num = 0; // num 存放计算后多位操作数的整数形式
int size = temp.size(); // 若直接将 temp.size()写在for循环中,则每执行一次i会增加,temp.size()会减少
for (int i = 0; i < size; ++i) {
num = num + (temp.top() - '0') * pow(10, i);
temp.pop();
if (temp.empty())
opd.push(num);
}
} else {
if (opr.empty())
opr.push(*it_exp);
else {
// 左括号,直接进栈
if (*it_exp == '(')
opr.push(*it_exp);
// 右括号,直到左括号前的运算符出栈,最后左括号出栈
else if (*it_exp == ')') {
while (opr.top() != '(') {
flag = calculate(opr, opd);
}
opr.pop(); // 左括号出栈
}
// 加减乘除运算符
else {
if (precede(*it_exp, opr.top()) == 1) // 当前扫描的运算符优先级高
opr.push(*it_exp);
else { // 当前扫描的运算符比栈顶运算符优先级低或者相同
flag = calculate(opr, opd);
opr.push(*it_exp); // 栈顶优先级高的运算符出栈后,优先级低的进栈
}
}
}
}
if (flag == 0)
break;
}
// 所有表达式扫描完后 opr栈中所有剩余的运算符出栈
while (!opr.empty()) {
flag = calculate(opr, opd);
if (flag == 0)
return 0;
}
int result = 0;
if (flag == 1) {
result = opd.top();
return result;
} else {
return 0;
}
}
int main()
{
string exp;
cout << "Please enter a positive integer expression:" << endl;
cin >> exp;
if (judge_exp_legality(exp) == true) {
if (evaluate_infix_expression(exp) == 1)
cout << evaluate_infix_expression(exp) << endl;
else
cout << "Zero divisor!" << endl;
} else {
cout << "Please enter a valid expression:" << endl;
cin >> exp;
}
return 0;
}
运行结果: