表达式求值---栈的应用(合法性的判断,中缀直接求法,中缀转后缀)

简述

此表达式求值,假设操作符只是整数,运算符值仅限于+,-,*,/,(,和#。
运算规则如下

  1. 先乘除,后加减
  2. 同级运算时,先左后右。
  3. 先括号内,后括号

实现(中缀直接实现)

  • 检验合法性

    • 括号的匹配,还有运算符的连续输入
      (这里括号的匹配是正确的,有一点小瑕疵,就是括号太多的话,可能会出错,还有就是运算符的连续输入这里,应该还有几种情况,鄙人才疏学浅,暂且想到了下面两种,敬请纠正)
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 +
  • 后缀表达式运算
    可以设置一个栈,从左到右扫描后缀表达式,每读到一个操作数就将它压入栈中,每读到一个运算符,就取出两个操作数进行运算,并将结果压入栈中,如此进行,直到后缀表达式读完,最后栈顶的就是计算结果。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值