求值算法,来自邓俊辉老师的数据结构
基于Stack的类模板,处理中缀表达式求值,和将中缀表达式转转换成后缀表达式
#include "Stack.h"//基于之前写的类模板Vector
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<cmath>
#include"Stack.h"
using namespace std;
#define N_OPTR 9 //运算符总数
typedef enum
{
ADD, SUB, MUL, DIV, POW, FAC, L_P, R_P, EOE,
} Operator; //运算符集合
//加、减、乘、除、乘方、阶乘、左括号、右括号、起始符与终止符
const char pri[N_OPTR][N_OPTR] =
{
//运算符优先等级 [栈顶] [当前]
/* |-------------------- 当 前 运 算 符 --------------------| */
/* + - * / ^ ! ( ) \0 */
/* -- + */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
/* | - */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
/* 栈 * */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
/* 顶 / */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
/* 运 ^ */ '>', '>', '>', '>', '>', '<', '<', '>', '>',
/* 算 ! */ '>', '>', '>', '>', '>', '>', ' ', '>', '>',
/* 符 ( */ '<', '<', '<', '<', '<', '<', '<', '=', ' ',
/* | ) */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
/* -- \0 */ '<', '<', '<', '<', '<', '<', '<', ' ', '='
};
void readNumber(char*& p, Stack<double>& stk)
{
//将起始于p的子串解析为数值,并存入操作数栈
stk.push((double)(*p - '0')); //当前数位对应的数值进栈
while (isdigit(*(++p))) //只要后续还有紧邻的数字(即多位整数的情况),则
{
stk.push(stk.pop() * 10 + (*p - '0')); //弹出原操作数并追加新数位后,新数值重新入栈
}
if ('.' != *p)
{
return; //此后非小数点,则意味着当前操作数解析完成
}
float fraction = 1; //否则,意味着还有小数部分
while (isdigit(*(++p))) //逐位加入
{
stk.push(stk.pop() + (*p - '0') * (fraction /= 10)); //小数部分
}
}
void append(char*& rpn, double opnd)
{
//将操作数接至RPN末尾
char buf[64];
if (0.0 < opnd - (int)opnd)
{
sprintf(buf, "%f \0", opnd); //浮点格式,或
}
else
{
sprintf(buf, "%d \0", (int)opnd); //整数格式
}
rpn = (char*)realloc(rpn, sizeof(char) * (strlen(rpn) + strlen(buf) + 1)); //扩展空间
strcat(rpn, buf); //RPN加长
}
void append(char*& rpn, char optr)
{
//将运算符接至RPN末尾
int n = strlen(rpn); //RPN当前长度(以'\0'结尾,长度n + 1)
rpn = (char*)realloc(rpn, sizeof(char) * (n + 3)); //扩展空间
sprintf(rpn + n, "%c ", optr);
rpn[n + 2] = '\0'; //接入指定的运算符
}
Operator optr2rank(char op)
{
//由运算符转译出编号
switch (op)
{
case '+':
{
return ADD; //加
}
case '-':
{
return SUB; //减
}
case '*':
{
return MUL; //乘
}
case '/':
{
return DIV; //除
}
case '^':
{
return POW; //乘方
}
case '!':
{
return FAC; //阶乘
}
case '(':
{
return L_P; //左括号
}
case ')':
{
return R_P; //右括号
}
case '\0':
{
return EOE; //起始符与终止符
}
default:
{
exit(-1); //未知运算符
}
}
}
char orderBetween(char op1, char op2) //比较两个运算符之间的优先级
{
return pri[optr2rank(op1)][optr2rank(op2)];
}
__int64 facI(int n)
{
__int64 f = 1;
while (n > 1)
{
f *= n--;
}
return f;
} //阶乘运算(迭代版)
double calcu(double a, char op, double b)
{
//执行二元运算
switch (op)
{
case '+':
{
return a + b;
}
case '-':
{
return a - b;
}
case '*':
{
return a * b;
}
case '/':
{
if (0 == b)
{
exit(-1);
}
else
{
return a / b; //注意:如此判浮点数为零可能不安全
}
}
case '^':
{
return pow(a, b);
}
default:
{
exit(-1);
}
}
}
double calcu(char op, double b)
{
//执行一元运算
switch (op)
{
case '!':
{
return (double)facI((int)b); //目前仅有阶乘,可照此方式添加
}
default:
{
exit(-1);
}
}
}
double evaluate(char* S, char*& RPN)
{
//对(已剔除白空格的)表达式S求值,并转换为逆波兰式RPN
Stack<double> opnd;
Stack<char> optr; //运算数栈、运算符栈 /*DSA*/任何时刻,其中每对相邻元素之间均大小一致
char* expr = S;
optr.push('\0'); //尾哨兵'\0'也作为头哨兵首先入栈
while (!optr.empty())
{
//在运算符栈非空之前,逐个处理表达式中各字符
if (isdigit(*S))
{
//若当前字符为操作数,则
readNumber(S, opnd);
append(RPN, opnd.top()); //读入操作数,并将其接至RPN末尾
}
else //若当前字符为运算符,则
{
switch (orderBetween(optr.top(), *S))
{
//视其与栈顶运算符之间优先级高低分别处理
case '<': //栈顶运算符优先级更低时
{
optr.push(*S); S++; //计算推迟,当前运算符进栈
break;
}
case '=': //优先级相等(当前运算符为右括号或者尾部哨兵'\0')时
{
optr.pop(); S++; //脱括号并接收下一个字符
break;
}
case '>':
{
//栈顶运算符优先级更高时,可实施相应的计算,并将结果重新入栈
char op = optr.pop(); append(RPN, op); //栈顶运算符出栈并续接至RPN末尾
if ('!' == op)
{
//若属于一元运算符
double pOpnd = opnd.pop(); //只需取出一个操作数,并
opnd.push(calcu(op, pOpnd)); //实施一元计算,结果入栈
}
else
{
//对于其它(二元)运算符
double pOpnd2 = opnd.pop(), pOpnd1 = opnd.pop(); //取出后、前操作数 /*DSA*/提问:
//可否省去两个临时变量?
opnd.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
}
break;
}
default:
{
exit(-1); //逢语法错误,不做处理直接退出
}
}
}
}
return opnd.pop(); //弹出并返回最后的计算结果
}
char* removeSpace ( char* s )
{
//剔除s[]中的白空格
char* p = s, *q = s;
while ( true )
{
while (isspace(*q))
{
q++;
}
if ( '\0' == *q )
{
*p = '\0';
return s;
}
*p++ = *q++;
}
}
//教材实例代码包中的测试用例
//值得注意的是这里所使用的测试用例需要在VS2019中点击属性然后手动设置命令参数
/*
int main(int argc, char* argv[])
{ //表达式求值(入口)
for (int i = 1; i < argc; i++)
{ //逐一处理各命令行参数(表达式)
system("cls");
printf("\nPress any key to evaluate: [%s]\a\n", argv[i]);
getchar();
char* rpn = (char*)malloc(sizeof(char) * 1);
rpn[0] = '\0'; //逆波兰表达式
double value = evaluate(removeSpace(argv[i]), rpn); //求值
printf("EXPR\t: %s\n", argv[i]); //输出原表达式
printf("RPN\t: [ %s]\n", rpn); //输出RPN
printf("Value\t= %f = %d\n", value, (int)value); //输出表达式的值
free(rpn);
rpn = NULL;
getchar();
}
system("pause");
return 0;
}
*/
int main()
{
char* rpn = (char*)malloc(sizeof(char) * 1);
rpn[0] = '\0'; //逆波兰表达式
double value = evaluate(const_cast < char*>("(1+2^3!-4)*(5!-(6-(7-(89-0!))))"), rpn); //求值
cout << value << endl;
cout << "the rpn is:" << rpn;
free(rpn);
rpn = NULL;
system("pause");
return 0;
}
不基于手打的Stack类模板,而是基于已有的库函数实现求值算法
处理中缀表达式求值,和将中缀表达式转转换成后缀表达式
#include<stack>
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
using namespace std;
#define N_OPTR 9 //运算符总数
typedef enum
{
ADD, SUB, MUL, DIV, POW, FAC, L_P, R_P, EOE,
} Operator; //运算符集合
//加、减、乘、除、乘方、阶乘、左括号、右括号、起始符与终止符
const char pri[N_OPTR][N_OPTR] =
{
//运算符优先等级 [栈顶] [当前]
/* |-------------------- 当 前 运 算 符 --------------------| */
/* + - * / ^ ! ( ) \0 */
/* -- + */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
/* | - */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
/* 栈 * */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
/* 顶 / */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
/* 运 ^ */ '>', '>', '>', '>', '>', '<', '<', '>', '>',
/* 算 ! */ '>', '>', '>', '>', '>', '>', ' ', '>', '>',
/* 符 ( */ '<', '<', '<', '<', '<', '<', '<', '=', ' ',
/* | ) */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
/* -- \0 */ '<', '<', '<', '<', '<', '<', '<', ' ', '='
};
void readNumber(char*& p, stack<double>& stk)
{
//将起始于p的子串解析为数值,并存入操作数栈
stk.push((double)(*p - '0')); //当前数位对应的数值进栈
double temp;//创建临时的数
while (isdigit(*(++p))) //只要后续还有紧邻的数字(即多位整数的情况),则
{
temp=stk.top(); stk.pop();
stk.push(temp* 10 + (*p - '0')); //弹出原操作数并追加新数位后,新数值重新入栈
}
if ('.' != *p)
{
return; //此后非小数点,则意味着当前操作数解析完成
}
float fraction = 1; //否则,意味着还有小数部分
while (isdigit(*(++p))) //逐位加入
{
temp=stk.top(); stk.pop();
stk.push(temp + (*p - '0') * (fraction /= 10)); //小数部分
}
}
void append(char*& rpn, double opnd)
{
//将操作数接至RPN末尾
char buf[64];
if (0.0 < opnd - (int)opnd)
{
sprintf(buf, "%f \0", opnd); //浮点格式,或
}
else
{
sprintf(buf, "%d \0", (int)opnd); //整数格式
}
rpn = (char*)realloc(rpn, sizeof(char) * (strlen(rpn) + strlen(buf) + 1)); //扩展空间
strcat(rpn, buf); //RPN加长
}
void append(char*& rpn, char optr)
{
//将运算符接至RPN末尾
int n = strlen(rpn); //RPN当前长度(以'\0'结尾,长度n + 1)
rpn = (char*)realloc(rpn, sizeof(char) * (n + 3)); //扩展空间
sprintf(rpn + n, "%c ", optr);
rpn[n + 2] = '\0'; //接入指定的运算符
}
Operator optr2rank(char op)
{
//由运算符转译出编号
switch (op)
{
case '+':
{
return ADD; //加
}
case '-':
{
return SUB; //减
}
case '*':
{
return MUL; //乘
}
case '/':
{
return DIV; //除
}
case '^':
{
return POW; //乘方
}
case '!':
{
return FAC; //阶乘
}
case '(':
{
return L_P; //左括号
}
case ')':
{
return R_P; //右括号
}
case '\0':
{
return EOE; //起始符与终止符
}
default:
{
exit(-1); //未知运算符
}
}
}
char orderBetween(char op1, char op2) //比较两个运算符之间的优先级
{
return pri[optr2rank(op1)][optr2rank(op2)];
}
__int64 facI(int n)
{
__int64 f = 1;
while (n > 1)
{
f *= n--;
}
return f;
} //阶乘运算(迭代版)
double calcu(double a, char op, double b)
{
//执行二元运算
switch (op)
{
case '+':
{
return a + b;
}
case '-':
{
return a - b;
}
case '*':
{
return a * b;
}
case '/':
{
if (0 == b)
{
exit(-1);
}
else
{
return a / b; //注意:如此判浮点数为零可能不安全
}
}
case '^':
{
return pow(a, b);
}
default:
{
exit(-1);
}
}
}
double calcu(char op, double b)
{
//执行一元运算
switch (op)
{
case '!':
{
return (double)facI((int)b); //目前仅有阶乘,可照此方式添加
}
default:
{
exit(-1);
}
}
}
double evaluate(char* S, char*& RPN)
{
//对(已剔除白空格的)表达式S求值,并转换为逆波兰式RPN
stack<double> opnd;
stack<char> optr; //运算数栈、运算符栈 /*DSA*/任何时刻,其中每对相邻元素之间均大小一致
char* expr = S;
optr.push('\0'); //尾哨兵'\0'也作为头哨兵首先入栈
while (!optr.empty())
{
//在运算符栈非空之前,逐个处理表达式中各字符
if (isdigit(*S))
{
//若当前字符为操作数,则
readNumber(S, opnd);
append(RPN, opnd.top()); //读入操作数,并将其接至RPN末尾
}
else //若当前字符为运算符,则
{
switch (orderBetween(optr.top(), *S))
{
//视其与栈顶运算符之间优先级高低分别处理
case '<': //栈顶运算符优先级更低时
{
optr.push(*S); S++; //计算推迟,当前运算符进栈
break;
}
case '=': //优先级相等(当前运算符为右括号或者尾部哨兵'\0')时
{
optr.pop(); S++; //脱括号并接收下一个字符
break;
}
case '>':
{
//栈顶运算符优先级更高时,可实施相应的计算,并将结果重新入栈
char temp=optr.top(); optr.pop();
char op =temp ; append(RPN, op); //栈顶运算符出栈并续接至RPN末尾
if ('!' == op)
{
//若属于一元运算符
double pOpnd = opnd.top(); //只需取出一个操作数,并
opnd.pop();
opnd.push(calcu(op, pOpnd)); //实施一元计算,结果入栈
}
else
{
//对于其它(二元)运算符
double pOpnd2 = opnd.top();opnd.pop();
double pOpnd1 = opnd.top(); opnd.pop();//取出后、前操作数 /*DSA*/提问:
//可否省去两个临时变量?
//不可以,函数参数计算的顺序是不确定的
opnd.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
}
break;
}
default:
{
exit(-1); //逢语法错误,不做处理直接退出
}
}
}
}
double temp=opnd.top();
opnd.pop();
return temp ;//弹出并返回最后的计算结果
}
char* removeSpace ( char* s )
{
//剔除s[]中的白空格
char* p = s, *q = s;
while ( true )
{
while (isspace(*q))
{
q++;
}
if ( '\0' == *q )
{
*p = '\0';
return s;
}
*p++ = *q++;
}
}
//教材实例代码包中的测试用例
//值得注意的是这里所使用的测试用例需要在VS2019中点击属性然后手动设置命令参数
/*
int main(int argc, char* argv[])
{ //表达式求值(入口)
for (int i = 1; i < argc; i++)
{ //逐一处理各命令行参数(表达式)
system("cls");
printf("\nPress any key to evaluate: [%s]\a\n", argv[i]);
getchar();
char* rpn = (char*)malloc(sizeof(char) * 1);
rpn[0] = '\0'; //逆波兰表达式
double value = evaluate(removeSpace(argv[i]), rpn); //求值
printf("EXPR\t: %s\n", argv[i]); //输出原表达式
printf("RPN\t: [ %s]\n", rpn); //输出RPN
printf("Value\t= %f = %d\n", value, (int)value); //输出表达式的值
free(rpn);
rpn = NULL;
getchar();
}
system("pause");
return 0;
}
*/
int main()
{
char* rpn = (char*)malloc(sizeof(char) * 1);
rpn[0] = '\0'; //逆波兰表达式
double value = evaluate(const_cast < char*>("(1+2^3!-4)*(5!-(6-(7-(89-0!))))"), rpn); //求值
cout << value << endl;
cout << "the rpn is:" << rpn;
free(rpn);
rpn = NULL;
system("pause");
return 0;
}
逆波兰表达式求值
double rpnEvaluation(char *expr){
//对RPN表达式的求值
stack<double> Stk;//存放操作数的栈
while((*expr)!='\0'){
//expr尚未扫描完毕,即不等于结束符号,这里的结束符号是'\0'
if((*expr)==' ') {
//这里的分隔符号是空格
expr++;//跳过分隔符号
continue;
}
if (isdigit(*expr))//
{
//若当前字符为操作数,则
readNumber(expr,Stk);//将操作数入栈
}else //若当前字符为运算符,则
{
char op =(*expr);//计算当前操作符
expr++;//运算完成之后计算表达式
if ('!' == op){
//如果是一元运算
expr++;//运算完成之后计算表达式
double number = Stk.top();
Stk.pop();
Stk.push(calcu(op, number)); //实施一元计算,结果入栈
}
else{
double pOpnd2 = Stk.top();
Stk.pop();
double pOpnd1 = Stk.top();
Stk.pop();
Stk.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
}
}
}
return Stk.top();
}