码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-08-13 23:39:42      阅读:374      评论:0      收藏:0      [点我收藏+]

标签:

中缀表达式及后缀表达式图解中说明了使用逆波兰式进行表达式求值的方法,这里使用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”进行测试,查看转换后的后缀表达式以及运算结果,程序支持在控制台中持续输入几组数据并计算结果。

技术分享


完整的代码:下载

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

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

标签:

原文地址:http://blog.csdn.net/u012501459/article/details/47623511

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!