栈的经典应用——表达式求值 / 计算器

  任何一个表达式都是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的, 统称它们为单词。一般地,操作数既可以是常数,也可以是被说明为变量或常量的标识符;运算 符可以分为算术运算符、关系运算符和逻辑运算符 3 类;基本界限符 有左右括号和表达式结束符 等。

算术四则运算遵循以下 3条规则:

(1)先乘除,后加减;
(2)从左算到右;
(3)先括号内,后括号外。
算符间的优先关系
θ₁ \ θ₂
-*/()#
>><<<>>
->><<<>>
*>>>><>>
/>>>><>>
(<<<<<=
)>>>>>>
#<<<<<=

        由规则(1),先进行乘除运算,后进行加减运算,所以有“ + ” < “ * ”;“ + ” < “ / ”;“ * ” > “ + ”;“  / ” > “ + ”等。

        由规则(2),运算遵循左结合性,当两个运算符相同时,先出现的运算符优先级高,所以有“ + ” >  “ + ” ; “ — ” > “ — ”;  “ * ” > “ * ” ;  “ / ” > “ / ” 。
        由规则(3),括号内的优先级高,+、—、*和/为θ₁时的优先性均低于“ ( ”但高于“ ) "。

        表中的“(”=“)”表示当左右括号相遇时,括号内的运算已经完成。为了便于实现,假设每个表达式均以“#”开始,以“#”结束。所以“#”=“#”表示整个表达式求值完毕。")”与“(”、“#”与“)”以及“(”与“#”之间无优先关系,这是因为表达式中不允许它们相继出现,一旦遇到这种情况,则可以认为出现了语法错误。

算法步骤:


①初始化OPTR栈和OPND栈,将表达式起始符“#”压入OPTR栈。

②扫描表达式,读入第一个字符ch,如果表达式没有扫描完毕至“#”或OPTR的栈顶元素不为“#”时,则循环执行以下操作:

    ●若ch不是运算符,则压入OPND栈,读入下一字符ch;

    ●·若ch是运算符,则根据OPTR 的栈顶元素和ch的优先级比较结果,做不同的处理;

        >若是小于,则ch 压入 OPTR栈,读人下一字符ch;

        >若是大于,则弹出OPTR栈顶的运算符,从OPND栈弹出两个数,进行相应运算,结果压入OPND栈;
        >若是等于,则OPTR的栈顶元素是“(”且ch是“)”,这时弹出OPTR栈顶的“(”,相当于括号匹配成功,然后读入下一字符ch。
③OPND栈顶元素即为表达式求值结果,返回此元素。

具体实现: 

#include <iostream>
#include<stack>//调用(STL stack)容器
#define OK 1
#define ERROR -1
#define OVERFLOW -2
using namespace std;
//判断输入的字符ch是否是运算符
bool In(char ch){
	if( ch=='+' || ch=='-' || ch=='*' || ch=='/' || ch=='(' || ch==')' || ch=='#' ){
		return true;
	}
	else return false;	
}
//判定运算符栈(OPTR)的栈顶元素与读入的运算符之间优先关系 
char Precede(char ch1,char ch2){
	if( ch1 =='+' || ch1 =='-' ){//判断第一个运算符 
		if( ch2 =='*' || ch2 =='/' || ch2 =='(' ){//判断第二个运算符 
			return '<';
		} 
		else return '>';
	}
	else if( ch1 =='*' || ch1 =='/'){
		if( ch2 =='(' ){
			return '<';
		}
		else return '>';
	}
	else if( ch1 =='(' ){
		if( ch2 ==')' ){
			return '=';
		}
		else if( ch2 =='#'){
			return '?';//出错 
		}
		else return '<';
	}
	else if( ch1 ==')' ){
		if( ch2 =='('){
			return '?';//出错 
		}
		else return '>'; 
	}
	else if( ch1 =='#' ){
        if( ch2 =='#' ){
            return '=';
        }
		else if( ch2 == ')' ){
            return '?';
        }
		else return '<';
    }
    else return '?';//其他的都return错误 
}
//进行二元运算的函数
int Operate(int a,char theta,int b){
	if(theta == '+' )
        return a+b;
    else if(theta == '-')
        return a-b;
    else if(theta == '*')
        return a*b;
    else if(theta == '/')
        return a/b;
    else return 0;
}
int CALCULATE(){
	stack<int> OPND;//运算数栈
    stack<char> OPTR;//运算符栈
    OPTR.push('#');//表达式起始"#"压入OPTR栈
    char ch;
    char theta;
    int a,b,num=0;
    cin >> ch;
/*	当表达式没有扫描完毕至表达式末尾的"#" 
	或OPTR(操作符栈)的栈顶元素不是"#"(他目前栈底元素是"#")时进入循环	*/
    while(ch != '#' || OPTR.top()!='#'){
        if(!In(ch)){//若ch不是运算符,则压入OPND(操作数)栈,读入下一个字符ch 
            num=num*10+(ch-'0');//0的ASCII码是48 
            cin >> ch;
            if(In(ch)) {
                OPND.push(num);
                num =0;
            }
        }
		else//若ch是运算符
		{
            switch (Precede(OPTR.top(),ch)){//比较OPTR(运算符)的栈顶元素和ch的优先级
                case '<':
                    OPTR.push(ch);//当前字符ch压入OPTR(运算符)栈,读入下一个字符ch
                    cin >> ch;
                    break;
                case '>':
                    theta = OPTR.top();  OPTR.pop();//弹出OPTR(运算符)栈顶元素符
                    b = OPND.top();  OPND.pop();//弹出OPND(操作数)栈顶的两个运算数
                    a = OPND.top();  OPND.pop();
                    OPND.push(Operate(a,theta,b));//将运算结果压入OPND(操作数)栈
                    break;
                case '='://"="只有一种情况,OPTR(操作数)的栈顶元素是"("且ch是")"
                    OPTR.pop();//弹出OPTR(操作数)栈顶的"(",消去一对括号,读入下一字符ch
                    cin >> ch;
                    break;
                default:
                    exit(ERROR);
            }
        }
    }
    cout << OPND.top();//求值结果
    return 0;
} 
int main(){
	CALCULATE();
}

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是关于应用表达式求值)的学习笔记。 ## 1. 表达式求值的基本概念 ### 1.1 中缀表达式 中缀表达式是我们平时写的表达式,如:2 + 3 * 4 - 6 / 2。 ### 1.2 后缀表达式 后缀表达式也叫逆波兰表达式,它是一种不包含括号的表达式。在后缀表达式中,运算在操作数的后面,因此也叫后缀表示法。例如,上面的中缀表达式的后缀表达式为:2 3 4 * + 6 2 / -。 ### 1.3 前缀表达式 前缀表达式也叫波兰式,它与后缀表达式类似,只是运算在操作数的前面。例如,上面的中缀表达式的前缀表达式为:- + * 3 4 2 / 6 2。 ### 1.4 运算优先级 在中缀表达式中,运算有不同的优先级。通常,乘法和除法的优先级高于加法和减法。如果有括号,则括号内的表达式优先计算。 ### 1.5 中缀表达式转后缀表达式 将中缀表达式转换成后缀表达式的过程,也叫中缀表达式的后缀表达式化。具体的转换规则如下: - 遍历中缀表达式的每个元素。 - 如果当前元素是操作数,则将其加入后缀表达式中。 - 如果当前元素是运算,则判断其与顶运算的优先级,如果顶运算优先级高于或等于当前运算,则弹出顶运算加入后缀表达式中,并继续比较下一个顶运算,直到当前运算的优先级高于顶运算为空时,将当前运算。 - 如果当前元素是左括号“(”,则直接入。 - 如果当前元素是右括号“)”,则依次弹出顶运算加入后缀表达式中,直到遇到左括号为止,此时将左括号弹出,但不加入后缀表达式中。 ### 1.6 后缀表达式求值 将后缀表达式求值的过程,也叫后缀表达式的求值。具体的求值规则如下: - 遍历后缀表达式的每个元素。 - 如果当前元素是操作数,则将其入。 - 如果当前元素是运算,则弹出顶的两个操作数,进行运算,并将运算结果入。 - 遍历完后缀表达式后,中只剩下一个元素,即为表达式的值。 ## 2. 表达式求值的实现 ### 2.1 中缀表达式转后缀表达式的实现 中缀表达式转后缀表达式可以使用来实现。具体的代码实现如下: ```cpp #include <iostream> #include <stack> #include <string> using namespace std; // 判断一个字是否为操作 bool isOperator(char c) { return c == '+' || c == '-' || c == '*' || c == '/'; } // 判断两个操作的优先级 int getPriority(char op1, char op2) { if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) { return -1; } else if ((op1 == '+' || op1 == '-') && (op2 == '*' || op2 == '/')) { return 1; } else { return 0; } } // 将中缀表达式转换成后缀表达式 string infixToPostfix(string infix) { stack<char> opStack; // 运算 string postfix; // 后缀表达式 for (char c : infix) { if (isdigit(c)) { // 如果是数字,直接加入后缀表达式 postfix += c; } else if (isOperator(c)) { // 如果是操作 while (!opStack.empty() && opStack.top() != '(' && getPriority(opStack.top(), c) >= 0) { postfix += opStack.top(); // 弹出顶操作加入后缀表达式 opStack.pop(); } opStack.push(c); } else if (c == '(') { // 如果是左括号,直接入 opStack.push(c); } else if (c == ')') { // 如果是右括号 while (!opStack.empty() && opStack.top() != '(') { postfix += opStack.top(); // 弹出顶操作加入后缀表达式 opStack.pop(); } opStack.pop(); // 弹出左括号 } } while (!opStack.empty()) { // 将剩余的操作加入后缀表达式 postfix += opStack.top(); opStack.pop(); } return postfix; } int main() { string infix = "2+3*4-6/2"; string postfix = infixToPostfix(infix); cout << postfix << endl; // 输出后缀表达式:234*+62/- return 0; } ``` ### 2.2 后缀表达式求值的实现 后缀表达式求值也可以使用来实现。具体的代码实现如下: ```cpp #include <iostream> #include <stack> #include <string> using namespace std; // 判断一个字是否为操作 bool isOperator(char c) { return c == '+' || c == '-' || c == '*' || c == '/'; } // 计算两个操作数的运算结果 int calculate(int a, int b, char op) { if (op == '+') { return a + b; } else if (op == '-') { return a - b; } else if (op == '*') { return a * b; } else { return a / b; } } // 计算后缀表达式的值 int evaluate(string postfix) { stack<int> numStack; // 操作数 for (char c : postfix) { if (isdigit(c)) { // 如果是数字,将其转换成整数并入 int num = c - '0'; numStack.push(num); } else if (isOperator(c)) { // 如果是操作 int b = numStack.top(); numStack.pop(); int a = numStack.top(); numStack.pop(); int result = calculate(a, b, c); numStack.push(result); } } return numStack.top(); } int main() { string postfix = "234*+62/-"; int result = evaluate(postfix); cout << result << endl; // 输出计算结果:8 return 0; } ``` ## 3. 总结 表达式求值中的应用是很常见的,掌握了这个知识点,对于编写计算器应用程序会有很大的帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值