转贴(忘了弄URL了,来自CSDN,上面的文字部分是转的,下面的实现是我的)
逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如(a+b)*(c+d)转换为ab+cd+*d+。
它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:
如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
(1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
(2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号"#"。
(3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
(4)如果不是数字,该字符则是运算符,此时需比较优先关系。
做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。
(5)重复上述操作(1)-(2)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
其中运算符优先级如下:
*/:4
+-:3
(:2
):1
中缀表达式到后缀表达式的转换
要把表达式从中缀表达式的形式转换成用后缀表示法表示的等价表达式,必须了解操作符的优先级和结合性。优先级或者说操作符的强度决定求值顺序;优先级高的操作符比优先级低的操作符先求值。如果所有操作符优先级一样,那么求值顺序就取决于它们的结合性。操作符的结合性定义了相同优先级操作符组合的顺序(从右至左或从左至右)。
转换过程包括用下面的算法读入中缀表达式的操作数、操作符和括号:
1. 初始化一个空堆栈,将结果字符串变量置空。
2. 从左到右读入中缀表达式,每次一个字符。
3. 如果字符是操作数,将它添加到结果字符串。
4. 如果字符是个操作符,弹出(pop)操作符,直至遇见开括号(opening parenthesis)、优先级较低的操作符或者同一优先级的右结合符号。把这个操作符压入(push)堆栈。
5. 如果字符是个开括号,把它压入堆栈。
6. 如果字符是个闭括号(closing parenthesis),在遇见开括号前,弹出所有操作符,然后把它们添加到结果字符串。
7. 如果到达输入字符串的末尾,弹出所有操作符并添加到结果字符串。
逆波兰表达式又称后缀式,是波兰逻辑学家J.Lukasiewicz于1929提出的一种后缀表达运算式的方法。区别于我们所熟悉的中缀式,后缀式的特点就是运算量在前,运算符在后,如a+b表达为ab+。这种后缀表达式非常方便去掉运算符优先级的影响与括号,甚至是单目运算符:
1. a*b+c*(d+e) => ab*cde+*+
2. -a+-b*+c => a!b!c#*+ (注:为区别与减法运算与加法运算,这里用!表示负号,#表示正号)
那么计算机怎样通过后缀式来进行运算呢?这里首先假设读取分析表达式的准备工作都已经做好了,那么首先需要做的是把表达式转换成后缀式,也就是逆波兰表达式的构建过程。
构建器由两个主要组件组成,一个是目标表达式的存储器,另一个是一个符号栈。与源表达式的扫描顺序一样,存储器是从左向右储存数据的,而符号栈则遵守后进先出的原则:
* 读入一个数据
1. 如果是单目运算符,直接入符号栈;
2. 如果是运输量,则直接写入存储器;检查符号栈顶是否有单目运算符,有的话则全部出栈,并写入存储器;
3. 如果是左括号"(",则直接入符号栈;
4. 如果是右括号")",则弹出符号栈数据,写入存储器,直到左括号弹出;
5. 如果是普通运算符,则与栈顶符号比较优先级,若大于栈顶优先级,则入栈;否则弹出栈顶符号并写入存储器,直到栈顶符号的运算优先级较小为止;
6. 如果是结束符(表示表达式已全部读完),则符号栈全部弹出并写入存储器,否则读取数据进入下个过程。
此外还有一些处理的技巧,比如定义一个优先级最低的运算符作为表达式结束的标志,在符号栈里首先加入一个结束标志,那么表达式读完时则自动弹出栈中所有符号,并写入存储器结尾表示成功。
下面用一个表格表示构建的过程:
Token
|
Expression
|
Dest Data
|
LIFO stack
|
Description
|
|
a*b+c*(d+-e)#
|
|
#
|
# is Ending Sign
|
a
|
*b+c*(d+-e)#
|
a
|
#
|
Value -> Data
|
*
|
b+c*(d+-e)#
|
a
|
#*
|
* > #
|
b
|
+c*(d+-e)#
|
ab
|
#*
|
Value -> Data
|
+
|
c*(d+-e)#
|
ab*
|
#
|
+ < *
|
|
c*(d+-e)#
|
ab*
|
#+
|
+ > #
|
c
|
*(d+-e)#
|
ab*c
|
#+
|
Value -> Data
|
*
|
(d+-e)#
|
ab*c
|
#+*
|
* > +
|
(
|
d+-e)#
|
ab*c
|
#+*
|
LPar( -> Stack
|
d
|
+-e)#
|
ab*cd
|
#+*(
|
Value -> Data
|
+
|
-e)#
|
ab*cd
|
#+*(+
< /td>
|
+ > (
|
!
|
e)#
|
ab*cd
|
#+*(+!
|
! -> Stack
|
e
|
)#
|
ab*cde!
|
#+*(+
|
Value -> Data, Pop !
|
)
|
#
|
ab*cde!+
|
#+*
|
RPar) -> Pop Until (
|
#
|
|
ab*cde!+*+#
|
|
# -> Pop Until #
|
接下来是计算的过程。计算的时候除了刚才构建的数据外,还需要另外一个数据栈。首先是从左至右扫描数据段,如果读出的是数据则压入数据栈,遇到符号时从数据栈中弹出相应的数据进行运算,再把结果压回数据栈。依然拿上面的结果做表格列示:
Token
|
Expression
|
Data Stack
|
Compute
|
Description
|
|
ab*cde!+*+#
|
|
|
|
a
|
b*cde!+*+#
|
a
|
|
Value -> Stack
|
b
|
*cde!+*+#
|
ab
|
|
Value -> Stack
|
*
|
cde!+*+#
|
|
a*b
|
Pop a, b
|
|
cde!+*+#
|
m
|
|
set m = a*b
|
c
|
de!+*+#
|
mc
|
|
Value -> Stack
|
d
|
e!+*+#
|
mcd
|
|
Value -> Stack
|
e
|
!+*+#
|
mcde
|
|
Value -> Stack
|
!
|
+*+#
|
mcd
|
!e
|
Pop e
|
|
+*+#
|
mcdn
|
|
set n = !e
|
+
|
*+#
|
mc
|
d+n
|
Pop d, n
|
|
*+#
|
mco
|
|
set o = c+n
|
*
|
+#
|
m
|
c*o
|
Pop c, o
|
|
+#
|
mp
|
|
set p = c*o
|
+
|
#
|
|
m+p
|
Pop m, p
|
|
#
|
r
|
|
set r = m+p
|
#
|
|
r
|
check
|
Accept!
|
这样,返回结果就是栈中唯一的数据,我们完成了逆波兰表达式的全部计算过程。
感觉对于做计算器的话,这个东西很有用啊
下面是我的实现:
首先实现一个栈
package myPolishNotation;
public class Stack<T> {
private StackNode<T> top;
private int index = -1;
public void push(T content){
StackNode<T> node = new StackNode<T>();
node.setContent(content);
node.setLink(top);
top = node;
index++;
}
public StackNode<T> pop(){
if(index==-1){
System.out.println("stack hasn't content");
return null;
}
StackNode<T> node = this.getTop();
top = node.getLink();
index--;
return node;
}
public StackNode<T> getTop() {
return top;
}
public int getIndex() {
return index;
}
}
栈的节点:
package myPolishNotation;
public class StackNode<T> {
private T content;
private StackNode<T> link;
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
public StackNode<T> getLink() {
return link;
}
public void setLink(StackNode<T> link) {
this.link = link;
}
}
最后实现
package myPolishNotation;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class PolishNotation {
public List<String> parseExpression(String expression){
//先将表达式分割成操作符与数字并放入temp列表
StringTokenizer st = new StringTokenizer(expression,"+-*/()",true);
List<String> temp = new ArrayList<String>();
while(st.hasMoreElements()){
temp.add((String)st.nextElement());
}
List<String> polisthNotationlist = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
for(String temp_ch : temp){
if(isOperator(temp_ch)){
//栈为空或操作符为(就入栈
if(stack.getIndex()==-1||"(".equals(temp_ch)){
stack.push(temp_ch);
}else{
//如果操作符为)就将栈内)之前(之后的操作符弹出并放入逆波兰列表,最后弹出(
if(")".equals(temp_ch)){
while(!stack.getTop().getContent().equals("(")){
String operator = stack.getTop().getContent();
polisthNotationlist.add(operator);
stack.pop();
}
stack.pop();
}else{
//如果栈顶元素为(,操作符入栈
if(stack.getTop().getContent().equals("(")){
stack.push(temp_ch);
}else{
//当栈不为空,且栈顶元素不为(时,比较操作符的优先级,如果栈顶元素优于当前操作符,那么弹出栈顶元素,直到栈为空或栈顶元素优先级低于当前操作符,
//则将当前操作符压入栈
while(stack.getIndex()!=-1&&!stack.getTop().getContent().equals("(")){
if(isPriority(operatorPriority(stack.getTop().getContent()),operatorPriority(temp_ch))){
String operator = stack.getTop().getContent();
polisthNotationlist.add(operator);
stack.pop();
if(stack.getIndex()==-1||!isPriority(operatorPriority(stack.getTop().getContent()),operatorPriority(temp_ch))){
stack.push(temp_ch);
break;
}
}else{
stack.push(temp_ch);
break;
}
}
}
}
}
}else{
//非操作符直接入栈
polisthNotationlist.add(temp_ch);
}
}
while(stack.getIndex()!=-1){
//栈中余下的操作符弹出
polisthNotationlist.add(stack.pop().getContent());
}
return polisthNotationlist;
}
//计算逆波兰表达式的值
public double calculatePolishNotation(List<String> polishNotation){
Stack<String> stack = new Stack<String>();
for(String str : polishNotation){
if(!this.isOperator(str)){
stack.push(str);
}else{
String number1 = stack.pop().getContent();
String number2 = stack.pop().getContent();
stack.push(String.valueOf(calculate(str, number2, number1)));
}
}
return Double.parseDouble(stack.pop().getContent());
}
//普通表达式计算
public double calculate(String operator,String number1,String number2){
char opt = operator.charAt(0);
double double1 = Double.parseDouble(number1);
double double2 = Double.parseDouble(number2);
switch(opt){
case '+':
return double1+double2;
case '-':
return double1-double2;
case '*':
return double1*double2;
case '/':
return double1/double2;
}
return 0;
}
//设置操作符优先级,()设不设无所谓
public int operatorPriority(String operator){
if("(".equals(operator)){
return 1;
}
if(")".equals(operator)){
return 2;
}
if("+".equals(operator)||"-".equals(operator)){
return 3;
}
if("*".equals(operator)||"/".equals(operator)){
return 4;
}
return 0;
}
//栈顶操作符与列表中操作符比较优先级
public boolean isPriority(int topOperator,int listOperator){
if(topOperator>=listOperator){
return true;
}
return false;
}
//判断是否为操作符
public boolean isOperator(String arg){
if("+-*/()".indexOf(arg)!=-1){
return true;
}
return false;
}
public static void main(String[] args) {
PolishNotation pn = new PolishNotation();
List<String> list = pn.parseExpression("4-(4-3*5+(2*4)+100)/10");
for(String str:list){
System.out.print(str+"\t");
}
System.out.println(pn.calculatePolishNotation(pn.parseExpression("4-(4-3*5+(2*4)+100)/10")));
}
}
不想看的就下载吧
分享到:
相关推荐
逆波兰表达式实现 逆波兰表达式是一种后缀表达式,通常用于表示数学表达式的计算顺序。它的特点是运算符在后面,先计算括号里的运算,然后计算乘除,最后计算加减。逆波兰表达式的实现可以通过栈或队列来实现。 逆...
逆波兰表达式,又称后缀表达式,是一种用于表示数学计算的符号表示法。它将操作符放在操作数之后,避免了使用括号,简化了运算过程。在基于逆波兰表达式的计算程序中,通常包括以下几个核心知识点: 1. **逆波兰...
### 逆波兰表达式及其算法实现 #### 一、引言 逆波兰表达式(Reverse Polish Notation, RPN)是一种特殊的数学表达式表示方法,它最初由波兰逻辑学家Jan Łukasiewicz提出。与传统的中缀表达式相比,逆波兰表达式...
逆波兰表达式,又称后缀表达式,是一种在编译原理和计算机科学中常见的表示算术和逻辑表达式的方式。它的主要特点是操作符位于其操作数之后,这与我们常用的中缀表达式(操作符位于操作数之间)不同。这种表示方式在...
C语言:设计一个算法,将一般算术表达式转化为逆波兰表达式,并求逆波兰表达式的值。数据结构实验
逆波兰表达式(Reverse Polish Notation,RPN)是一种没有括号且运算符放在操作数之后的数学表达式表示方式,常用于计算器设计。在这个简易的灰色逆波兰表达式计算器中,我们主要涉及以下几个关键知识点: 1. **逆...
逆波兰表达式是一种特殊的数学表示法,也称为后缀表达式。它将操作符放置在操作数之后,以此避免使用括号。这种表示法在计算和解析时特别有用,因为无需考虑运算符优先级,只需按照操作数出现的顺序进行计算即可。在...
逆波兰表达式,又称后缀表达式,是一种在计算领域广泛应用的数学表示方式,它将操作符放在操作数之后,避免了使用括号来明确运算优先级。这种表示方法非常适合用栈来处理,因为可以直观地按照“后进先出”(LIFO)的...
逆波兰表达式(Reverse Polish Notation,RPN)算法是一种基于后缀表示法的计算方法,主要用于解决数学表达式的求值问题。它将运算符放置在操作数之后,避免了括号的使用,使得表达式求值的过程更为直观。在这个算法...
逆波兰表达式求值是计算机科学中的一个重要概念,主要用于避免使用括号来表示运算优先级,从而简化表达式的解析过程。逆波兰表达式,又称后缀表达式,是一种没有括号、无需考虑运算符优先级的数学表达式书写方式。在...
逆波兰表达式,又称后缀表达式,是一种用于表示数学计算的符号表示法。它将操作符放在操作数之后,避免了使用括号来决定运算的优先级,从而简化了解析过程。本教程将介绍两种在C/C++或Linux环境下求解逆波兰表达式的...
易语言源码逆波兰表达式计算易语言源码.rar 易语言源码逆波兰表达式计算易语言源码.rar 易语言源码逆波兰表达式计算易语言源码.rar 易语言源码逆波兰表达式计算易语言源码.rar 易语言源码逆波兰表达式计算易语言...
逆波兰表达式,又称后缀表达式,是一种不使用括号的数学表达式表示方法,它使用栈的数据结构来解析和计算表达式。在逆波兰表达式中,运算符位于其操作数之后,使得表达式的解析更为简单。本篇文章将详细讲解如何用...
逆波兰表达式(Reverse Polish Notation,RPN)是一种数学表达式表示方法,它不需要使用括号,而是通过运算符后置的方式明确运算顺序。在RPN计算器中,运算符紧跟在其操作数之后,使得计算过程更为简洁。这种表示法...
### 利用堆栈实现逆波兰表达式(后缀法) #### 一、逆波兰表达式简介 逆波兰表达式,又称后缀表达式或后缀记法,是一种没有括号且运算符位于操作数之后的数学表达式书写方式。在计算机科学中,这种表达式被广泛...
逆波兰表达式的长 度不超过一行,以 "$"作为输入结束,操作数之间用空格分隔,操作符只可能有+、—、*、/四种 运算。例如: 23434 + 2*$。 数据结构作业
【数据结构与算法】逆波兰表达式完整版,使用java语言编写。逆波兰表达式又叫做后缀表达式,是一种没有括号,并严格遵循“从左到右”运算的后缀式表达方法