作为栈的初学者,表达式求值的问题给我带来了非常多的思考。目前,我还只能实现非负整数的带加减乘除模及括号运算的表达式求值,且效率也不算高。但还是想记录一下这个问题实现的整个过程。
我们平常习惯的表达式称为中缀表达式,是语法树中序遍历的结果,这样用计算机来计算并不容易,特别是遇到括号中还有很多括号的情况。也就是说,我们面对的最大的问题是,当不知道有多少个优先级需要判断时,我们到底该怎么办呢?前人真的非常的聪明,想到转换成后缀表达式进行求值,而这个转换的过程让我颇有受益。
在所有问题之前,我们先确定符号的优先级。
int priority(char op){
if(op == '+' || op == '-') return 1;
else if(op == '*' || op == '/' || op == '%') return 2;
else return 0;
}//运算符优先级。
接下来我们先实现中缀表达式到后缀表达式的转换。
创建
char* op = (char*)malloc(sizeof(char)*1000000);
char** RPN = (char**)malloc(sizeof(char*)*1000000);
int i = 0, j = 0, k, t = 0, top = -1;//op为我们储存符号的栈,RPN为我们要转换成的逆波兰表达式。
对所有可能遇到的字符进行处理
if(s[k] == 32) continue;//说明:s为传入的表达式字符串,k为已经遍历到的第k位字符。
//如果是空格我们直接跳过。
else if(s[k] >= '0' && s[k] <= '9'){
RPN[j] = (char*)malloc(sizeof(char)*20);
RPN[j][t] = s[k];
t++;
while(k+1 < strlen(s) && s[k+1] >= '0' && s[k+1] <= '9'){
RPN[j][t] = s[k+1];
t++;
k++;
}
RPN[j][t] = '\0';
j++;
t = 0;
}//我们用j来记录逆波兰表达式中元素的个数。如果是非负整数,我们直接将其放入逆波兰表达式中,我们其实关键的是要处理符号的顺序
else if(s[k] == '('){
top++;
op[top] = s[k];
}//如果遇到左括号,我们需要将其放入符号的栈中,以来判断接下来符号入逆波兰表达式的顺序,括号中运算符应该优先入逆波兰表达式,先进行计算。
while(s[k] == '+' || s[k] == '-' || s[k] == '*' || s[k] == '/' || s[k] == '%'){
if(top == -1 || op[top] == '(' || priority(s[k])>priority(op[top])){
top++;
op[top] = s[k];
break;
}//如果表达式右边的运算符优先级高,进入符号栈,出栈时会优先左边先进的符号。
else{
RPN[j] = (char*)malloc(sizeof(char)*2);
RPN[j][0] = op[top];
RPN[j][1] = '\0';
j++;
top--;
}//将运算级高的符号优先入逆波兰表达式,先进行计算。
}
if(s[k] == ')'){
while(op[top]!='('){
RPN[j] = (char*)malloc(sizeof(char)*2);
RPN[j][0] = op[top];
RPN[j][1] = '\0';
j++;
top--;
}//将括号中的运算符提前出栈,确保其优先级最高。
top--; //舍弃左括号。
}
while(top!=-1){
RPN[j] = (char*)malloc(sizeof(char)*2);
RPN[j][0] = op[top];
RPN[j][1] = '\0';
j++;
top--;
} //遍历完表达式之后,将符号栈中的运算符出栈进入逆波兰表达式。
转换成逆波兰表达式之后,问题变得简单,我们只要遍历逆波兰表达式,遇到符号,将其前面的两个数进行运算即可。最后进行逆波兰表达式求值
创建
int *stack;
stack = (int*)malloc(sizeof(int) * j);
top = 0;//之前定义过top了,这里直接归0,创建一个储存整数的栈,直到遇到符号我们再进行运算。
处理每一个运算符
for(i = 0; i < j; i++){
if((RPN[i][0] >= '0' && RPN[i][0] <= '9') || RPN[i][1] != '\0'){
stack[top] = atoi(RPN[i]);
top++;
}//如果是数,直接进入整数栈进行储存。
else{
switch(RPN[i][0]){
case '*':
stack[top-2] = stack[top-1] * stack[top-2];
break;
case '/':
stack[top-2] = stack[top-2] / stack[top-1];
break;
case '%':
stack[top-2] = stack[top-2] % stack[top-1];
break;
case '+':
stack[top-2] = stack[top-2] + stack[top-1];
break;
case '-':
stack[top-2] = stack[top-2] - stack[top-1];
break;
default:
break;
}
top--;
}
}//遇到符号就对前两个非负整数进行运算。
return stack[0];//输出最后的结果。
当然这个计算器,目前还无法进行包含负整数的运算以及一些其他简单的功能,在效率方面也会继续学习一下再进行改进。进行前行咯。