简述
此表达式求值,假设操作符只是整数,运算符值仅限于+,-,*,/,(,和#。
运算规则如下
- 先乘除,后加减
- 同级运算时,先左后右。
- 先括号内,后括号
实现(中缀直接实现)
-
检验合法性
- 括号的匹配,还有运算符的连续输入
(这里括号的匹配是正确的,有一点小瑕疵,就是括号太多的话,可能会出错,还有就是运算符的连续输入这里,应该还有几种情况,鄙人才疏学浅,暂且想到了下面两种,敬请纠正)
- 括号的匹配,还有运算符的连续输入
int IsRight:(const char *) string {
char ch;
int len;
int top = 0;
len = (int)strlen(string);
for (int i = 0; i < len; i++) {
ch = string[i];
if (ch == '(') {
top++;
}
if (ch == ')') {
if (top == 0) {
return 0;
} else {
top--;
}
}
}
if (top != 0) {
return = 0;
} else {
;
}
for (int i = 0; i < len - 1; i++) {
if (string[i] == ')' && !Inopset(string[i + 1])) {
return 0;
}
if (Inopset(string[i]) && Inopset(string[i + 1]) && string[i] != '(' && string[i] != ')' && string[i + 1] != '(' && string[i + 1] != ')') {
return = 0;
}
}
}
- 原理:
首先,设置两个栈,一个存储运算符,一个存储操作数。先将‘#’存入运算符栈底,- 如果读入的是操作数,直接存入操作数栈中。读入下一个符号
- 如果读入的是运算符,就要比较和运算符栈顶元素比较优先级了
- 优先级大于栈顶元素,压入栈中,继续读入下一个符号
- 优先级小于栈顶元素,将操作数栈中退出两个操作数,然后在运算符栈中退出一个运算符,然后进行运算,(这里应注意这两个操作数的顺序,分别应该在运算符的左右的位置)并将运算的结果压入操作数栈中,而此时读入的运算符,下次重新考虑,即此时,不读入下一个符号
- 优先级相等,也就是左右括号相遇了,此时直接在运算符栈中退出栈顶元素即可,继续读入下一个符号
- 如果读入的运算符为‘#’,并且栈顶元素也为‘#’,说明已经做完运算了,此时,操作数栈的栈顶元素,即为运算结果。
- 注意操作数运算符的得到
if(!Inopset(ch)) {
flag = 0;
data = ch - '0';
ch = getchar();
while(!Inopset(ch)) {
if(ch == '.') {
flag = 1;
ch = getchar();
continue;
if(flag) {
data = data + (ch - '0') * pow(10, -flag); //小数的处理
flag++;
} else {
data = data * 10 + ch - '0'; //大于10的数的处理
}
ch = getchar();
}
PushNum(Num_top, data);
}
- 代码
int Exp() {
Num *Num_top;
OPTR *optr_top;
char ch, opch;
double data, num1, num2, res;
int flag = 0;
Num_top = InitNum();
optr_top = InitOPTR();
PushOPTR(optr_top, '#');
printf("输入\n");
ch = getchar();
while(ch != '#' || GetOPTR(optr_top) != '#' ) {
if(!Inopset(ch)) {
flag = 0;
data = ch - '0';
ch = getchar();
while(!Inopset(ch)) {
if(ch == '.') {
flag = 1;
ch = getchar();
continue;
}
if(flag) {
data = data + (ch - '0') * pow(10, -flag);
flag++;
} else {
data = data * 10 + ch - '0';
}
ch = getchar();
}
PushNum(Num_top, data);
} else {
switch(compare(GetOPTR(optr_top), ch)) {
//当前读入的大于栈顶运算符,压入栈中 ,继续读入下一个
case 1:
PushOPTR(optr_top, ch);
ch = getchar();
break;
//当前读入的小于栈顶运算符, 连续退出两个操作数,从运算符中退出一个运算符,进行运算,放入操作栈中
case 2:
// print(Num_top, optr_top);
num1 = PopNum(Num_top);
num2 = PopNum(Num_top);
opch = PopOPTR(optr_top);
// res = num2 op chnum1;
res = Execute(num2, opch, num1);
printf("%d%d", num1, num2);
printf("res%d\n", res);
PushNum(Num_top, res);
break;
//此时读入的运算符重新考虑,即不重新读入
case 3:
//相等,即左右括号相遇,只需将栈顶运算符退出即可
opch = PopOPTR(optr_top);
ch = getchar();
break;
}
}
}
res = GetNum(Num_top);
return res;
}
中缀转后缀
其实原理和上面差不太多,就是转成后缀后,然后进行运算。
- 转换规则
(图源,实验室一成员) - eg 2+3*(5-6)+4
后缀表达式:左–>右
符号栈B:栈底------------>栈顶
A:2 ; B: + ;(符号栈为空,直接将符号压入栈中)
A:2 3 ;B:+ * ;(栈顶符号为 ‘+’, 小于读到的’*‘,压入栈中
A:2 3 ;B:+ * ( ;(读到左括号,直接压入栈中)
A:2 3 5; B: + *( -;(左括号优先级小于‘-’,压入栈中)
A:2 3 5 6 -;B;+ * ;(读到右括号,将栈顶元素放到表达式中去,直到遇到左括号,并且不将左括号放到表达式中)
A:2 3 5 6 - * + 4;B ;(读到‘+’,栈顶优先级大于,将栈顶符号,pop出去,放到表达式中,)
A:2 3 5 6 - * + 4 +; B:(读到‘+’, 栈此时为空,现存入到栈中,然后因为,中缀表达式已经扫描完成,所以将栈中符号一次存入到表达式中); - 转换的另一种方法,
- 先按照运算符的优先级,对中缀表达式加括号,如上例,变成((2+(3*(5-6)))+4)
- 然后将运算符移到括号的后面,变成((2(3 (5 6)-*+)4)+
- 去掉括号得到:2 3 5 6 - * + 4 +
- 后缀表达式运算
可以设置一个栈,从左到右扫描后缀表达式,每读到一个操作数就将它压入栈中,每读到一个运算符,就取出两个操作数进行运算,并将结果压入栈中,如此进行,直到后缀表达式读完,最后栈顶的就是计算结果。