// 计算表达式的值,
// 两个栈一个放运算符(char),一个放数据(double)
// 先读进整个表达式,然后处理(处理采用量化,用二维数组使占用多位的数字只占用一位),
// 显然表达式不会被破坏
// 优先级的定义如果是 后面的符号优先级大于前面的符号优先级 则入栈
// 否则出栈进行计算,计算完后再入栈
// 如果遇到'(' ')'的情况则消除括号,')'不会入栈
// 最后算法结束 是遇到了表达式的末位标志 '\1'(这个是我自行定义的,其实改成'='更好)
// 并且返回数据栈顶的元素值 即可.
//
/*test data
4
(321.215+516.0210)*654+513.32=
(((21.2*554)-(154*2))/(11-93))=
(1+2+3+4)=
1+2-(4+5)=
548065.66
-139.47
10.00
-6.00
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
const int MAX_STACK = 4000; // 栈的大小
const int MAX_LENGTH = 1000; // 表达式的长度
struct EXPS{
char sym[MAX_STACK]; //operation
int top,bottom; // bottom可以用0代替 下同
}StackSym;
struct EXPN{
double num[MAX_STACK]; //double max length is 15
int top,bottom;
}StackNum;
char EXpre[MAX_LENGTH][17]; // 量化表达式
bool IsFull(EXPS *st){
return st->top == (MAX_STACK + 1);
}
bool IsFull(EXPN *st){
return st->top == (MAX_STACK + 1);
}
bool IsEmpty(EXPS *st){
return st->top == st->bottom;
}
bool IsEmpty(EXPN *st){
return st->top == st->bottom;
}
bool IsOper(char s){
return (s=='+'||s=='-'||s=='*'||s=='/'||s=='('||s==')');
}
bool CharIsNum(char s){ // 单个字符是不是0~9之间的数字
return (s <= '9' && s >= '0') || s == '.' ;
}
bool IsNum(char s){
return (s <= '9' && s >= '0');
}
void InStack(EXPN *st,double g){ // 把 数值g 入栈
if (!IsFull(st))
st->num[(st->top)++] = g;
else{ printf("爆栈了\n");exit(0);}
}
void InStack(EXPS *st,char g){ // 把 运算符g 入栈
if (!IsFull(st))
st->sym[(st->top)++] = g;
else{ printf("爆栈了\n");exit(0);}
}
double OutStack(EXPN *st){
if(!IsEmpty(st))
return st->num[--(st->top)]; // 先减再出
else printf("栈空不能进栈了\n");exit(0);
}
char OutStack(EXPS *st){
if(!IsEmpty(st))
return st->sym[--(st->top)] ; // 先减再出
else printf("栈空不能进栈了\n");exit(0);
}
char FrontStack(EXPS *st){ // 获取栈前元素,不出栈
return st->sym[(st->top)-1];
}
double FrontStack(EXPN *st){ // 获取栈前元素,不出栈
return st->num[(st->top)-1];
}
double CharToDouble(char * str){ // 将字符串转换为double
double s = 0,r = 0.1; // r控制小数点后,t控制小数点前
int sry,t=1;
(str[0]=='-')?(str++,sry = -1):(sry = 1); //判断正负
while ( *str >= '0' && *str <= '9' ){ // 整数部分 遇到小数点或者就结束了
s *= t,s += *str - '0',t = 10;
str++;
}
if (*str++ == '.')
while( *str >= '0' && *str <= '9' ){
s += r * (*str - '0'), r /= 10;
str++;
}
return s *= sry;// 加载正负号
}
void init(){ // 初始化
memset(StackSym.sym,0,sizeof(StackSym));
memset(StackNum.num,0,sizeof(StackNum));
StackSym.top = StackSym.bottom = 0;
StackNum.top = StackNum.bottom = 0;
}
void ExpressAnalysis(char* express){ // 表达式 量化,使得每一个运算符和数值都只需要一步操作
int k = 0,i=0;EXpre[i++][1] = '(';
while(*express != '='){
if (CharIsNum( *express) ||
(*express == '-' && k == 0 && (!IsNum( *(express-1))) && IsNum( *(express+1)))){
//如果这个 '-' 左边不是数字,右边是数字则算作负号
EXpre[i][k++] = *express;
if (!CharIsNum(*(express+1)))
{ EXpre[i][k] = '\0'; i++;}
}else{
k = 0;
EXpre[i++][1] = *express; // 运算符存在 第二位
}
express++;
}
EXpre[i++][1] = ')';
EXpre[i++][1] = '\1';
// (均为char型)表达式 (((21.2*554)-(154*2))/(11-93))=
// 表达式将会这样子存在 $ ( ( ( 2 * 5 ) - ( 1 * 2 ) ) / ( 1 - 9 ) ) = $
// 1 5 5 1 3
// . 4 4
// 2
}
double calc(double a,char t,double b){ // 计算 a 't' b
switch(t){
case '+' :return a + b;
case '-' :return a - b;
case '*' :return a * b;
case '/' :return a / b;
default: return 0.00;
}
}
char Priority(char a, char b){ // '<' 表示优先则进栈 '>'表示该计算了 '='表示遇到了括号 '0'表示表达式有错误
/* 优先级列表( $表示 '\1' )
+ - * / ( ) $ 当前
+ > > < < < > >
- > > < < < > >
* > > > > < > >
/ > > > > < > >
( < < < < < = 0
) 0 0 0 0 0 0 0 这个貌似不会进栈
$ < < < < < 0 0
栈首
*/
char PRO[7][7]={
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'<', '<', '<', '<', '<', '=', '0'},
{'0', '0', '0', '0', '0', '0', '0'},
{'<', '<', '<', '<', '<', '0', '0'}};
int row,col;
char pro[7] = {'+','-','*','/','(',')','\1'};
for (int i = 0 ; i < 7; i++){
if (a == pro[i]){ // 当前 对应列
row = i;
}
if (b == pro[i]){
col = i; // 栈中 对应行
}
}
return PRO[col][row];
}
double CalculationExpress(char* express){ // 计算这个表达式
/******算法*******
如果是数字就进栈
如果是运算符就判断
优先则进栈,否则
出栈计算,结果进栈
*******算法*******/
ExpressAnalysis(express); // 获得 EXpre[][]
int k = 0;
InStack(&StackSym,'\1');//先进一个 $ 东西
while( EXpre[k][1] != '\1'){
if ( IsOper( EXpre[k][1]) ){ // 第二位是操作符
char temp_pr1 = EXpre[k][1];
char temp_pr2 = FrontStack(&StackSym);
char psym = Priority(temp_pr1,temp_pr2); // psym: priortiy symble
double temp1,temp2,res;
switch(psym){
case '<' : // 运算符入栈
InStack(&StackSym,temp_pr1);
k++;
break;
case '=' : // 除括号
OutStack(&StackSym);
k++;
break;
case '>' : // 计算
temp2 = OutStack(&StackNum); // 后进先出
temp1 = OutStack(&StackNum);
temp_pr2 = OutStack(&StackSym);
res = calc(temp1,temp_pr2,temp2);
InStack(&StackNum,res);
//InStack(&StackSym,temp_pr2); //可能会拆括号 优先级发生改变
break;
case '0' :printf("这表达式貌似有错\n");exit(0);//退出程序
}
}else if ( IsNum( *EXpre[k]) || *EXpre[k] == '-' ){ // 第一位 是数字或者第二位是数字,因为存在'-'号 则转换后入栈
double temp = CharToDouble( EXpre[k] );
InStack(&StackNum,temp);
k++;
}else{
printf("输入非法\n");
exit(0);//退出程序
}
}
return FrontStack(&StackNum);
}
int main()
{
// freopen("in.txt","r",stdin);
int Ncase;
char express[MAX_LENGTH];
scanf("%d",&Ncase);
while(Ncase--){
init();
scanf("%s",&express);
double res = CalculationExpress(express);
printf("%.2lf\n",res);
}
return 0;
}
以上代码 可以用来AC NYOJ 35-表达式求值问题
/*
这里偶然发现一个函数 ecvt()可以将double转换为char型
void DoubleToChar(double s,char* str){ // 把double转成 char,损失了小数点
int decpt, sign, ndig = 15,i;
char *string;
//decpt参数指出给出小数点位置的整数值, 它是从该字符串的开头位置计算的。0或负数指出小数点在第一个数字的左边。
//sign参数指出一个指出转换的数的符号的整数。如果该整数为0,这个数为正数,否则为负数。
string = ecvt(s, ndig, &decpt, &sign);
for (i = 0; string[i]!='\0' ; i++)
str[i] = string[i];
str[i] = string[i];
}
*/