您的位置:首页 > 理论基础 > 数据结构算法

[数据结构] 表达式求值(转化为后缀表达式再求值或直接求值)

2016-03-05 17:23 561 查看
两种情况:

1. 根据输入的算数表达式,如(56-20) /(4+2), 先转化为后缀表达式(逆波兰式)56#20#-4#2#+/  因为输入的数字有多位数的(56),所以数之间用#隔开,然后根据后缀表达式求值。

2.根据输入的算数表达式,直接进行求值。

对于情况1:

转化为后缀表达式时用到了一个符号栈,把后缀表达式存放到数组postExp中,根据后缀表达式求值时用到了一个运算数栈,运算完后,栈顶即为所求。

完整代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;

const int maxOp = 7;//符号的总数

struct Pri //设定运算符优先级
{
char ch; //运算符
int pri; //优先级
}lpri[] = {{'=', 0}, {'(', 1}, {'*', 5}, {'/', 5}, {'+', 3}, {'-', 3}, {')', 6}},
rpri[] = {{'=', 0}, {'(', 6}, {'*', 4}, {'/', 4}, {'+', 2}, {'-', 2}, {')', 1}};

int LeftPri(char op)//求左运算符op的优先级
{
for(int i = 0; i < maxOp; ++ i)
if(lpri[i].ch == op)
return lpri[i].pri;
}

int RightPri(char op)//求右运算符op的优先级
{
for(int i = 0; i < maxOp; ++ i)
if(rpri[i].ch == op)
return rpri[i].pri;
}

bool IsOp(char ch)//判断符号ch是否为运算符
{
if(ch == '(' || ch == ')' || ch == '+' || ch == '-' || ch == '*' || ch == '/')
return true;
return false;
}

int Precede(char op1, char op2) //op1和op2运算符优先级的比较结果
{
int lPri = LeftPri(op1);
int rPri = RightPri(op2);
if(lPri == rPri)
return 0;
else if(lPri < rPri)
return -1;
else
return 1;
}

void TransToPostExp(char* exp, char postExp[]) //将算数表达式exp转换成后缀表达式
{
stack<char>opStack;//运算符栈
opStack.push('=');// =入栈,其优先级最低
int i = 0;//后缀表达式字符指针
while(*exp != '\0')
{
if(!IsOp(*exp)) //如果不是运算符
{
while(*exp >= '0' && *exp <= '9')//是数字
{
postExp[i ++] = *exp;
exp ++;
}
postExp[i ++] = '#'; //用'#'表示一个数值串的结束,比如456#
}
else
{
int cmpPri = Precede(opStack.top(), *exp);
if(cmpPri == 0) //左右运算符优先级相等,也就是()这种情况
{
opStack.pop();// '('退栈
exp ++;
}
else if(cmpPri == -1) //栈顶运算符优先级低
{
opStack.push(*exp);//入栈,符号栈栈顶始终是优先级最高的
exp ++;
}
else //栈顶运算符优先级高
{
while(Precede(opStack.top(), *exp) == 1)//不断退栈
{
postExp[i ++] = opStack.top();
opStack.pop();
}
//opStack.push(*exp);
// exp ++; 一定要注意!不能这样写,因为有可能当前字符是')'
}
}
}
while(opStack.top() != '=')//exp扫描完毕,退栈到' = '为止
{
postExp[i ++] = opStack.top();
opStack.pop();
}
postExp[i] = '\0';//postExp表达式添加结束标识
}

void GetTwoFromStack(stack<double>& numStack, double& a, double& b)//从运算数栈中获取两个数,栈顶和次栈顶
{
a = numStack.top();
numStack.pop();
b = numStack.top();
numStack.pop();
}

float CalFromPostExp(char* postExp)//计算后缀表达式的值
{
stack<double> numStack;//运算数栈
double a, b;
while(*postExp != '\0')
{
switch(*postExp)
{
case '#':
break;
case '+':
GetTwoFromStack(numStack, a, b);//获取运算数栈的两个数
numStack.push(b + a);//进行运算,再压入运算数中
break;
case '-':
GetTwoFromStack(numStack, a, b);
numStack.push(b - a);
break;
case '*':
GetTwoFromStack(numStack, a, b);
numStack.push(b * a);
break;
case '/':
GetTwoFromStack(numStack, a, b);
if(a != 0)
numStack.push(b / a);
else
{
cout << "除0错误! " <<endl;
exit(0);
}
break;
default: //处理数字字符
int n = 0;
while(*postExp >= '0' && *postExp <= '9')
{
n = n * 10 + (*postExp - '0');
postExp ++;
}
numStack.push(n);
break;
}
postExp ++; //继续处理字符
}
return numStack.top();
}

char exp[50];
char postExp[50];

int main()
{
while(cin >> exp)
{
cout << "中缀表达式为: " << exp <<endl;
TransToPostExp(exp, postExp);//将算数表达式转化为后缀表达式,比如输入(56-20)/(4+2) ,输出 56#20#-4#2#+/
cout << "后缀表达式为: " << postExp <<endl;
cout << "表达式的值为: " <<CalFromPostExp(postExp) << endl << endl;
}
return 0;
}

及其容易出现错误的地方为void TransToPostExp(char* exp, char postExp[]) //将算数表达式exp转换成后缀表达式   这个函数中:
else //栈顶运算符优先级高
{
while(Precede(opStack.top(), *exp) == 1)//不断退栈
{
postExp[i ++] = opStack.top();
opStack.pop();
}
//opStack.push(*exp);
// exp ++; 一定要注意!不能这样写,因为有可能当前字符是')'
}

一定要注意如果当前字符时')'时,它的右优先级是最低的,因为除了'('外的其它符号 + - * / 遇到它都得退出符号栈
不要把')'加入到符号栈中,opStack.push(*exp);

也不要直接忽略')'而继续扫描下一个字符,因为此时栈顶可能时'(',需要进行下一轮的比较,把'('出栈, 所以不能写 exp ++.

也就是说 这里需要特别考虑 当前字符是 ')'的情况

对于情况2:

用到了两个栈,符号栈和运算数栈,对输入的运算数表达式边扫描边求值。

完整代码(来来回回改了N多遍,  支持 ((((3+2))))这种情况) :

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;

const int maxOp = 7;//符号的总数

struct Pri //设定运算符优先级
{
char ch; //运算符
int pri; //优先级
}lpri[] = {{'=', 0}, {'(', 1}, {'*', 5}, {'/', 5}, {'+', 3}, {'-', 3}, {')', 6}},
rpri[] = {{'=', 0}, {'(', 6}, {'*', 4}, {'/', 4}, {'+', 2}, {'-', 2}, {')', 1}};

int LeftPri(char op)//求左运算符op的优先级
{
for(int i = 0; i < maxOp; ++ i)
if(lpri[i].ch == op)
return lpri[i].pri;
}

int RightPri(char op)//求右运算符op的优先级
{
for(int i = 0; i < maxOp; ++ i)
if(rpri[i].ch == op)
return rpri[i].pri;
}

bool IsOp(char ch)//判断符号ch是否为运算符
{
if(ch == '(' || ch == ')' || ch == '+' || ch == '-' || ch == '*' || ch == '/')
return true;
return false;
}

int Precede(char op1, char op2) //op1和op2运算符优先级的比较结果
{
int lPri = LeftPri(op1);
int rPri = RightPri(op2);
if(lPri == rPri)
return 0;
else if(lPri < rPri)
return -1;
else
return 1;
}

void GetTwoFromStack(stack<double>& numStack, double& a, double& b)//从运算数栈中获取两个数,栈顶和次栈顶
{
a = numStack.top();
numStack.pop();
b = numStack.top();
numStack.pop();
}

double CalFromNumStack(stack<char>& opStack, stack<double>& numStack)
//从符号栈提取栈顶符号,利用运算数栈的栈顶和次栈顶进行计算,并把结果压入运算数栈中
{
    double a, b;
    char op = opStack.top();//运算符提取
    opStack.pop();//退栈
    GetTwoFromStack(numStack, a, b);//a,b为栈顶和次栈顶
    switch(op)
    {
    case '+':
        numStack.push(b + a);
        break;
    case '-':
        numStack.push(b - a);
        break;
    case '*':
        numStack.push(b * a);
        break;
    case '/':
        numStack.push((b / a));
        break;
    }//switch
}

float CalFromExp(char* exp)
{
stack<char> opStack;//符号栈
opStack.push('=');//'='优先级最低
stack<double> numStack;//运算数栈
while(*exp != '\0')
{
if(!IsOp(*exp)) //如果不是运算符
{
int n = 0;//提取数字
while(*exp >= '0' && *exp <= '9')
{
n = n * 10 + (*exp - '0');
exp ++;
}
numStack.push(n);//把提取出来的数压入运算数栈
}
else
{
int cmpPri = Precede(opStack.top(), *exp);
if(cmpPri == 0) // ')'遇到栈顶'('
{
opStack.pop();
exp ++;
}

else if(cmpPri == -1)// 栈顶运算符优先级小
{
opStack.push(*exp);
exp ++;
}
else //栈顶运算符优先级大
{
while(Precede(opStack.top(), *exp) == 1)
{
CalFromNumStack(opStack,numStack);
}//while
}
// exp ++;//扫描下一个字符
}//当前字符是运算符
}

while(opStack.top() != '=')//千万不能忘了这个!!
{
CalFromNumStack(opStack,numStack);
}
return numStack.top();
}
char exp[50];
char postExp[50];

int main()
{
while(cin >> exp)
{
cout << "中缀表达式为: " << exp <<endl;
//TransToPostExp(exp, postExp);//将算数表达式转化为后缀表达式,比如输入(56-20)/(4+2) ,输出 56#20#-4#2#+/
// cout << "后缀表达式为: " << postExp <<endl;
cout << "表达式的值为: " <<CalFromExp(exp) << endl << endl;
}
return 0;
}

及其容易出现错误的地方和情况1一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: