使用逆波兰式进行表达式求值

中缀表达式及后缀表达式图解中说明了使用逆波兰式进行表达式求值的方法,这里使用C++进行实现。实现和原理讲解有一点不同,需要进一步进行细化。

关于将中缀表达式转换成后后缀表达式的规则:

规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于找顶符号(乘除优先加减)则栈顶元素依次出找并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。

上面的规则转换成下面的执行规则:

1.遇到操作数:直接输出(添加到后缀表达式中)

2.栈为空时,遇到运算符,直接入栈

3.遇到左括号:将其入栈

4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。

5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈

6.最终将栈中的元素依次出栈,输出。

需要在流程中对上面6种情况下进行判断并作相应的处理,并且由于数字可能有多位,所以对操作数的取值也是一个问题。

//比较lhs的优先级是否不高于rhs,rhs表示栈顶的符号
bool priority(char lhs,char rhs)
{
		if (rhs=='(')
			return false;
		if (lhs=='+'||lhs=='-')
			return true;
		if ((lhs=='*'||lhs=='/')&&(rhs=='*'||rhs=='/'))
			return true;
		return false;
}

//将中缀表达式转换成后缀式
string inPrefix2postPrefix(string str)
{
	string res;//后缀表达式结果
	stack<char> s;
	for (int i=0;i<str.size();i++)
	{
		//如果是数字,直接加入后缀表达式结果中
		if (isdigit(str[i]))
		{
			while (i<str.size()&&isdigit(str[i]))
			{
					res+=str[i];
					i++;
			}
			i--;//注意这里要将i减1,因为上面的循环将i多右移了一位,如果不减1,会漏掉一位
			res+=" ";
		}
		else //如果是符号,需要与栈顶的元素进行比较
		{
			 //如果栈为空,将其直接压入栈中;如果是左括号(,也直接将其压入栈中
				if (s.empty()||str[i]=='(')
						s.push(str[i]);
				else
				{
						//当碰到右括号时,将栈中的数据出栈,直到碰到左括号,注意左右括号都不需要加入结果res中
						if (str[i]==')')
						{
								while (!s.empty()&&s.top()!='(')//注意在对栈执行top操作之前需要判断栈是否为空
								{
									res+=s.top();
									res+=" ";
									s.pop();
								}
								s.pop();
						}
						else
						{
								//此时表示该字符为符号,并且不为'('和')'
								if (priority(str[i],s.top()))//如果它的优先级不高于栈顶符号,那么将栈顶符号出栈
								{
										while(!s.empty()&&priority(str[i],s.top()))
										{
												res+=s.top();
												res+=" ";
												s.pop();
										}
										s.push(str[i]);//最后记得将该符号入栈
								}
								else //如果它的优先级比栈顶符号高,那么直接入栈
										s.push(str[i]);
						}
				}
		}
	}
	while(!s.empty())//遍历完字符串后将栈中剩余的元素加入结果集中
	{
		res+=s.top();
		res+=" ";
		s.pop();
	}
	return res;
}

上面是将中缀表达式转换成后缀表达的方法,接下来是处理后缀表达式的方法:

  • 规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。

需要注意从栈顶弹出的第一个元素是第一个操作数,弹出的第二个元素是第二个操作数,不要把顺序弄反了。此时栈用来进出运算的数字。

int operate(int first,int second,char op)
{
	int res=0;
	switch (op)
	{
		case '+':
			res= first+second;
			break;
		case '-':
			res=first-second;
			break;
		case '*':
			res=first*second;
			break;
		case '/':
			res=first/second;
			break;
		default:
			break;
	}
	return res;
}
int   calculateByPostPrefix(string input)
{
	stack<int> s;
	int tmp=0;
	for (int i=0;i<input.size();i++)
	{
		if (isdigit(input[i]))//如果遇到的是数字,就将数字入栈
		{
			while(i<input.size()&&isdigit(input[i]))
			{
				tmp=10*tmp+input[i]-'0';
				i++;
			}
			//得到数字以后将这个输入压入栈中
			s.push(tmp);
			i--;
		}
		else if(input[i]==' ')//如果遇到空格,就将tmp重置为0
			tmp=0;
		else//此时遇到的就是符号
		{
			   //取出两个操作数,并进行计算
				int second=s.top();
				s.pop();
				int first=s.top();
				s.pop();
				int local=operate(first,second,input[i]);
				s.push(local);
		}
	}
	return s.empty()?0:s.top();
}

主函数如下:

int main()
{
	string str;//9+(3-1)*3+10/2
	cout<<"请输入合法的表达式(支持整数的+,-,*,/,括号运算):"<<endl;
	while(getline(cin,str))
	{
		string ot=inPrefix2postPrefix(str);
		cout<<"后缀表达式为:"<<ot<<endl;
		cout<<"计算结果为:"<<calculateByPostPrefix(ot)<<endl;
		cout<<"请输入表达式:"<<endl;
	}
	system("pause");
	return 0;
}

取前一篇文章中的例子“9+(3-1)*3+10/2”进行测试,查看转换后的后缀表达式以及运算结果,程序支持在控制台中持续输入几组数据并计算结果。

完整的代码:下载

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-23 13:48:21

使用逆波兰式进行表达式求值的相关文章

逆波兰式与表达式求解

/*************** 逆波兰式即后缀表示法 预处理 ---- 中序表达式->逆序表达式(infix to postfix) 算法: while(表达式非空) if (遇到操作数) 直接输出 else if (遇到操作符op) op是( 直接入栈s op是) s.push输出,直到( op是四则运算,则 if (s为空 || s.top为( || op 优先级高于 s.top) op 入栈 else s.push输出 if (!s.empty) s.push输出 计算 算法: if (

js实现 - 逆波兰式

没有任何方法,除非你才华横溢. --艾略特 js实现 - 逆波兰式 2019-05-26 by 文科生 最近编译原理实验有涉及到逆波兰式,而且听闻有人在前端面试过程中被问到逆波兰式算法的实现,之前的离散数学课程中也有涉及到逆波兰式,作为一名前端人员,终于按耐不住想用js去实现求逆波兰式的算法.我查阅了大量的资料,发现有的算法虽然基本实现了对应的功能,但在细节处理方面略显不妥:而有的算法写的过于庞杂,想要完全读懂则代价昂贵,而且代码的臃肿不是因为算法本身复杂,而是加入了一些算符,这对理解算法本质是

表达式求值(只包括小括号)

表达式求值 前缀式:就是将操作符放到数值的前面:如:a+b :  +ab: 中缀式:就是将操作符放在数值中间,其实就是我们平时生活中所写的正常的表达式.如:a+b: 后缀式:就是将操作符放在数值的后面,比如:a+b:——ab+. 对于表达式求值,最简单的当然是对后缀表达式(也称为逆波兰式)进行求值了. 而我们生活中所写的运算表达式,一般都是中缀表达式.但是我们计算的时候用到的却是后缀表达式.这就需要我们首先将中缀表达式转换为后缀表达式,然后再进行运算了. 1.中缀式转换为后缀式 下面是将中缀式(

从中序表达式到逆序表达式(逆波兰式)(四则运算表达式求值)

本份代码需要两个栈.一个是符号栈,一个是数字栈. 输入中序表达式如9+(3-1)*3+10/2# #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<math.h> #define max 100 char ex[max]; /*存储后序表达式*/ /*函数功能:将中序表达式转化为后序表达式*/ void trans() { char str[max]; /*表达式字符串*/ char stack[max]; /*运

逆波兰表达式求值

根据逆波兰表示法,求表达式的值. 有效的运算符包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说明: 整数除法只保留整数部分. 给定逆波兰表达式总是有效的.换句话说,表达式总会得出有效数值且不存在除数为 0 的情况. 示例 1: 输入: ["2", "1", "+", "3", "*"] 输出: 9 解释: ((2 + 1) * 3) = 9 示例 2: 输入: [&qu

lintcode 中等题:Evaluate Reverse Polish notation逆波兰表达式求值

题目 逆波兰表达式求值 在逆波兰表达法中,其有效的运算符号包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰计数表达. 样例 ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 ["4", "13", "5", "/", "+"]

栈应用——逆波兰式表达式的值

问题描述: 计算给定的逆波兰表达式(即后缀表达式)的值. 事实上,二元运算的前提下,中缀表达式可以对应一棵二叉树:逆波兰式即该二叉树后序遍历的结果. 分析思路: 如果当前是操作数,则直接入栈: 如果当前是操作符,则栈顶的两个元素弹出,然后与当前操作符运算后入栈. Code: /** * 给出一个逆波兰式,计算该表达式的值 * @param s * @return */ public int getTheValueOfRPN(String s) { char[] ch = s.toCharArra

LeetCode:逆波兰表达式求值【150】

LeetCode:逆波兰表达式求值[150] 题目描述 根据逆波兰表示法,求表达式的值. 有效的运算符包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说明: 整数除法只保留整数部分. 给定逆波兰表达式总是有效的.换句话说,表达式总会得出有效数值且不存在除数为 0 的情况. 示例 1: 输入: ["2", "1", "+", "3", "*"] 输出: 9 解释: ((2

[Swift]LeetCode150. 逆波兰表达式求值 | Evaluate Reverse Polish Notation

Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, *, /. Each operand may be an integer or another expression. Note: Division between two integers should truncate toward zero. The given RPN expression