代码如下:
#include"DataStructure.hpp"
#include<iostream>
using namespace std;
/**
* @brief 显示两个运算符的优先级别
* @param 运算符1,表达式中前面的运算符
* @param 运算符2,表达式中后面的运算符
* @retval 返回相对优先级别,op1>op2:1,op1<op2:-1,op1=op2:0
*/
int op_cmp(char op1, char op2)
{
int val = 2;
switch (op1)
{
case '+':
{
switch (op2)
{
case '+':val = 1; break;
case '-':val = 1; break;
case '*':val = -1; break;
case '/':val = -1; break;
case '(':val = -1; break;
case ')':val = 1; break;
case '#':val = 1; break;
default:
break;
}
}break;
case '-':
{
switch (op2)
{
case '+':val = 1; break;
case '-':val = 1; break;
case '*':val = -1; break;
case '/':val = -1; break;
case '(':val = -1; break;
case ')':val = 1; break;
case '#':val = 1; break;
default:
break;
}
}break;
case '*':
{
switch (op2)
{
case '+':val = 1; break;
case '-':val = 1;break;
case '*':val = 1;break;
case '/':val = 1;break;
case '(':val = -1;break;
case ')':val = 1;break;
case '#':val = 1;break;
default:
break;
}
}
case '/':
{
switch (op2)
{
case '+':val = 1; break;
case '-':val = 1; break;
case '*':val = 1; break;
case '/':val = 1; break;
case '(':val = -1; break;
case ')':val = 1; break;
case '#':val = 1; break;
default:
break;
}
}break;
case '(':
{
switch (op2)
{
case '+':val = -1; break;
case '-':val = -1; break;
case '*':val = -1; break;
case '/':val = -1; break;
case '(':val = -1; break;
case ')':val = 0; break;
default:
break;
}
}break;
case ')':
{
switch (op2)
{
case '+':val = 1;break;
case '-':val = 1;break;
case '*':val = 1;break;
case '/':val = 1;break;
case '(':val = 1;break;
case '#':val = 1;break;
default:
break;
}
}
case '#':
{
switch (op2)
{
case '+':val = -1; break;
case '-':val = -1; break;
case '*':val = -1; break;
case '/':val = -1; break;
case '(':val = -1; break;
case '#':val = 0; break;
default:
break;
}
}break;
default:
break;
}
return val;
}
/**
* @brief 计算一个只含一个操作符的算式
* @param 前一个操作数
* @param 后一个操作数
* @param 操作符
* @retval 返回结果
*/
int Caculate(int pre, int next, char op)
{
int Retval;
switch (op)
{
case '+':
Retval = pre + next;
break;
case '-':
Retval = pre - next;
break;
case '*':
Retval = pre * next;
break;
case '/':
Retval = pre / next;
break;
default:
break;
}
return Retval;
}
/**
* @brief 表达式求解的主要函数。
* @param 表达式以字符串的形式传入
* @retval 返回最终结果
*/
int GetExpVal(string exp)
{
int val = 65535;
Stack<char> optr;
Stack<int>opnd;
optr.push('#');
int i = 0;
while (exp[i] != '#'||optr.gettopdata()!='#')
{
if (exp[i] <= '9' && exp[i] >= '0')
{
opnd.push(exp[i] - '0');
i++;
}
else
{
int rel = op_cmp(optr.gettopdata(), exp[i]);
if (rel == 0)
{
optr.pop();
i++;
}
else if (rel == 1)
{
char op;
int pre, next;
op = optr.pop();
next = opnd.pop();
pre = opnd.pop();
int tempval = Caculate(pre, next, op);
opnd.push(tempval);
}
else if (rel == -1)
{
optr.push(exp[i]);
i++;
}
}
}
val = opnd.gettopdata();
return val;
}
int main()
{
cout <<"1+2*(3-4)="<< GetExpVal("1+2*(3-4)#") << endl;
cout << "1*(5-4)/2=" << GetExpVal("1*(5-4)/2#") << endl;
return 0;
}
其中的栈操作代码如下:
#pragma once
#include<iostream>
using namespace std;
#define MaxSize 100
//初始化,压栈,入栈
template<class T>
class Stack {
private:
int top;
T data[MaxSize];
public:
Stack();
T pop();
void push(T);
void PrintStack();
T gettopdata();
};
#include"DataStructure.hpp"
template<class T>
Stack<T>::Stack()
{
this->top = -1;
}
template<class T>
void Stack<T>::PrintStack()
{
int index = this->top;
if (index == -1)
{
cout << "栈空!" << " ";
}
while (index >= 0)
{
cout << this->data[index] << " ";
index--;
}
}
template<class T>
void Stack<T>::push(T data)
{
if (this->top + 1 == MaxSize)
{
cout << "栈满" << " ";
return;
}
this->data[++this->top] = data;
}
template<class T>
T Stack<T>::pop()
{
if (this->top < 0)
{
cout << "栈空" <<" ";
return -1;
}
this->top--;
return this->data[this->top + 1];
}
template<class T>
T Stack<T>::gettopdata()
{
return this->data[top];
}
void Stacktest()
{
Stack<int> s;
s.push(10);
s.push(11);
s.push(12);
s.push(13);
s.PrintStack();
}
我使用的表达式求值方法貌似与逆波兰求值法有一些不同,是按照严蔚敏教材上的方法做的,讲解如下:
首先我们了解一下写代码的思路,我们知道,在我们自己进行四则运算时,主要遵循以下三条法则:
1、先乘除后加减
2、从左往右算
3、先算括号内,再算括号外的。
于是我们得到如下表:
注意,我们为了方便,在算术中引入了‘#’符号作为标识符,以便操作。并且,由法则2,我们可以得到在有连续两个操作符相等的情况下,前一个操作符的优先级更大。
我们定义好优先级后,就会想到如何实现求解表达式求值,这里我们使用了两个栈。一个是操作数栈,一个是操作符栈,为了操作方便,我们先在操作符压入一个‘#’,同时,我们也要在表达式后缀加一个‘#’。如代码所示
in GetExpVal function()
optr.push('#');
in main funtion();
cout <<"1+2*(3-4)="<< GetExpVal("1+2*(3-4)#") << endl;
这里我们用一个循环读取字符串,进入循环终止条件为当读到的字符是‘#’并且操作符栈中也只剩下‘#’时(这时候我们将所以的表达式都读取了并且我们把所有的操作的执行了),循环结束。
在循环中,我们先判断是否为数字,如果是数字,则压入操作数栈,如果是操作符,则有三种情况:
第一种情况是栈顶的操作符优先级小于读入的操作符,此时将读入的操作符压栈。(优先级低的后执行,根据栈的特性,肯定是越靠近栈顶的操作越先被执行,所以优先级大越大的,在越上面)。
第二种情况则是相等,从我们的优先级图中,我们可以知道,相等只有两种情况:‘#’–‘#’和‘(’–‘)’,如果是‘#’和‘#’,证明表达式已经结束,即符合循环终止条件,所以只有可能是‘(’–‘)’,啥时候会出现左右括号是连续的?当然是括号中的运算已经完成了,所有操作符都出栈了,那么此时我们就不将读入的字符(即‘)’)入栈,而是将栈顶元素‘(’出栈,这样就完成了一次括号内的运算。
第三种情况则是大于,根据优先级高的先执行,那么此时我们的栈顶元素就是优先级最高的操作符,所以应该执行运算操作,所以我们此时应该将操作数的前两个元素出栈,操作符中的前一个元素出栈,进入计算函数,并将计算后的结果压入操作数栈中(我们在手动计算表达式时,也会将中间值再次代入到表达式中)。
如此,直到读取完字符串中的元素并且将操作符栈的元素都执行了后,就结束循环。