RPN逆波兰表达式(后缀表达式)
逆波兰表达式,它的语法规定,表达式必须以逆波兰表达式的方式给出。逆波兰表达式又叫做后缀表达式。这个知识点在数据结构和编译原理这两门课程中都有介绍,下面是一些例子:
正常的表达式 逆波兰表达式
a+b ---> a,b,+
a+(b-c) ---> a,b,c,-,+
a+(b-c)*d ---> a,b,c,-,d,*,+
a+d*(b-c)--->a,d,b,c,-,*,+
a=1+3 ---> a,1,3,+,=
用途
逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如(a+b)(c+d)转换为ab+cd+
定义很清楚,那么表达式:12*(3+4)-6+8/2 ,我们可以转换为:12 3 4 + * 6 - 8 2 / + ,既然将其转换为了逆波兰表达式,那么有该如何进行求值,这里,应用栈就可以特别方便的将其求出,其思路便是,将数字入栈,遇到算术操作符,取出前两个入栈的数字进行运算,在再将结果入栈。将12 3 4 + * 6 - 8 2 / +分步操作。
将 12 3 4入栈
遇到算数操作符 “+”,将 3 4 取出,相加得 7,将结果7入栈,而后再遇“*”,再将 7 12 取出相乘 结果为 84
将 84 入栈,其后的是数字6,将其入栈,接下来是算术操作符 “-”,取出 6 84
计算 84 -6
将结果78入栈,随后是数字8和2,分别入栈,然后又遇到算术操作符 “/”
将 2 和8取出,计算结果为4,入栈,再往后走,遇到了操作符“+”,那么再将4和78取出相加
最后将计算结果82入栈,那么当所有操作结束,栈中所存的元素便是我们计算所得的最终结果。
代码
package stack;
interface Stack {
void stackPush(Object obj);
void stackPop();
int stackSize();
Object stackTop();
void printLink();
}
class Factory {// 工厂模式实例化StackImpl类
private Factory() {
}
public static Stack getLinkInstance() {
return new StackImpl();
}
}
class StackImpl implements Stack {// 继承了Stack接口的方法,通过向上转型,产生实例化对象
private int size = 0;// Stack入栈元素的数量
private Node first;// 栈底,
private Node last;// 栈顶
private class Node {// 产生节点的内部类
private Object item;// 存放入栈元素
private Node next;// 放下一次要入栈的节点
private Node(Object item, Node next) {// 构造方法
this.item = item;
this.next = next;
}
}
@Override
public void stackPush(Object obj) {// 入栈方法
Node tmp = this.first;
Node newnode = new Node(obj, null);
this.last = newnode;
if (this.first == null) {// 第一次入栈,设栈底
this.first = newnode;
} else {
while (null != tmp.next) {// 非第一次入栈,遍历找栈顶,入栈
tmp = tmp.next;
}
tmp.next = newnode;
}
this.size++;// 记得扩充元素数量
}
@Override
public void stackPop() {// 出栈
Node tmp = this.first;
if(null ==tmp.next) {//当栈中只有一个元素时,直接清空栈
this.first = null;
this.last = null;
this.size = 0;
return;
}
while (null != tmp.next.next) {// 遍历栈,找离栈顶一个节点的节点
tmp = tmp.next;
}
this.last = tmp;// 重设栈顶
tmp.next = null;
this.size--;
}
@Override
public int stackSize() {// 获取当前栈中节点数(元素数量)
return this.size;
}
@Override
public Object stackTop() {// 获取栈顶元素
return this.last.item;
}
@Override
public void printLink() {// 辅助方法,查看当前栈中的所有元素
Node tmp = this.first;
while (null != tmp) {
System.out.println(tmp.item);
tmp = tmp.next;
}
}
}
public class Test {
public static void main(String[] args) {
Stack stack = Factory.getLinkInstance();//调用工厂类get方法,产生一个StackImpl类实例,向上转型实现Stack接口
String str = "12 3 4 + * 6 - 8 2 / +";
RPN(str, stack);//逆波兰表达式求值方法
//+++++++++++++++++++++++
//测试入栈
// stack.stackPush(1);
// stack.stackPush("2");
// stack.stackPush("3");
// stack.stackPush("4");
// stack.stackPush("5");
// stack.stackPush("6");
// stack.printLink();
// System.out.println("++++++++++++++++++++++++++");
// //+++++++++++++++++++++++++++++++++
// //测试出栈
// // link.stackPop();
// stack.printLink();
// System.out.println("++++++++++++++++++++++++++");
// //+++++++++++++++++++++++++++++++++
// //测试获取栈顶元素和元素数量
// System.out.println(stack.stackTop());
// System.out.println(stack.stackSize());
}
public static void RPN(String str,Stack stack) {
int left = 0;//用来计算的左值
int right = 0;//用来计算的由值
char [] strarr = new char [str.length()];//创建一个字符数组,长度str.length()获取字符串str的字符串长度
strarr = str.toCharArray();//这里的toCharArray()方法将字符串转为字符数组
int num = 0;//用来存转换后的数字
for (int i = 0; i <strarr.length; i ++ ) {//遍历
while (' ' != strarr[i] && i < strarr.length && '0' <= strarr[i] && strarr[i] <= '9') {// "12 3 4 + * 6 - 8 2 / +";
//过滤,只留数字字符进入
num = num * 10 + strarr[i]-'0';//转化,字符数字与数字ASCII码差48,即'0'
i++;//这个i就是for中的i,大家同步走
}
if (' ' == strarr[i] && '0' <= strarr[i-1] && strarr[i-1] <= '9') {//当前为空格,且前一个是数字字符(可能为+-..)
stack.stackPush(num);//关键操作,把数字入栈
}
num = 0;//清空,不然后果很严重
// String s = String.valueOf('c'); //效率最高的方法,字符转字符串
switch (strarr[i]) {//是算术操作符,进入计算
case '+':{
right = Integer.parseInt(stack.stackTop().toString() ) ;//对象转int,先转为字符串,再转为数字Object o ;Integer.praseInt(o==null?"":o.toString())
//栈顶返回值是对象,采用曲线救国的方式,先转成字符串,在转为数字
stack.stackPop();//将已经使用过的元素出栈
left = Integer.parseInt(stack.stackTop().toString() ) ;
stack.stackPop();
stack.stackPush(left+right);//将计算结果入栈
}
break;
case '-':{
right = Integer.parseInt(stack.stackTop().toString() ) ;
stack.stackPop();
left = Integer.parseInt(stack.stackTop().toString() ) ;
stack.stackPop();
stack.stackPush(left-right);
}
break;
case '*':{
right = Integer.parseInt(stack.stackTop().toString() ) ;//对象转int,先转为字符串,再转为数字Object o ;Integer.praseInt(o==null?"":o.toString())
stack.stackPop();
left = Integer.parseInt(stack.stackTop().toString() ) ;
stack.stackPop();
stack.stackPush(left*right);
}
break;
case '/':{
right = Integer.parseInt(stack.stackTop().toString() ) ;//对象转int,先转为字符串,再转为数字Object o ;Integer.praseInt(o==null?"":o.toString())
stack.stackPop();
left = Integer.parseInt(stack.stackTop().toString() ) ;
stack.stackPop();
try {//异常处理,right可能为零
left = left/right;
stack.stackPush(left);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();//啥都不写
}
}
break;
default:
break;
}
// System.out.println(Integer.parseInt(stack.stackTop().toString() ));//检测每次入栈情况
}
System.out.println(Integer.parseInt(stack.stackTop().toString() ));
}
}
这里的栈的构造参见>JAVA实现动态栈<,在每行代码后面都做了详细的注释,这里的代码也一样,但是由于里面加入了逆波兰表达式求值的方法,所以看起来难免会觉得乱,>源码已上传Github<,如果觉得JAVA语言看起来太乱,可以参考>C语言应用栈实现逆波兰表达式求值(动态栈和求值源码)<