计算表达式的值

// 计算表达式的值,
// 两个栈一个放运算符(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];
}
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值