利用数组模拟栈实现简单的表达式中缀转后缀并进行计算

来源:互联网 发布:淘宝盗图处罚规则2016 编辑:程序博客网 时间:2024/06/10 03:30

学习自《Java 数据结构与算法》第二版

前景概要:

不了解栈的概念请参考上一篇《用数组模拟栈结构,实现分隔符"{}"、"()"、"[]"的左右匹配检查

1,中缀表达式:中缀表达式即我们平常使用的表达式形式 如:3+(4+5)


2,后缀表达式:后缀表达式也称逆序表达式,它是由波兰的一位数学家发明的,与中缀表达式不同的是
它将操作符跟在操作数的后面,例如:3+4 用后缀表达式表示就是 34+ ,在者,如1的后缀表达式为 345++

3,为什么使用后缀表达式?
中缀表达式在计算时要根据运算的优先级进行先后运算,转成后缀表达式后,计算机可以根据运算符出现的
前后顺序直接进行运算而不必再去讨论它的优先级,方便了计算机的计算,因为计算机习惯于从左至右的读取。

实现:


实现第一步:中缀转后缀(难点)

实现中缀转后缀的前提是要找到中缀转后缀过程中的规律。人类在进行转换时可以直接跳过优先级低的计算优先级高的,当是计算机不行,计算机需要满足从左至右的规律进行计算,下面通过一个实例来演示计算机的处理过程:
模拟计算机转换A*(B+C)的过程:


分析:
解析  A 后缀(H):复制A
解析 *H:后缀需要两个数字才能跟一个操作符,故暂不处理,(这时应该思考暂待处理的操作符应该存在哪
解析( H:该操作符优先级高于*,故仍旧不能处理*,此时未处理的包括 *(
解析 B 后缀(H): 复制B
解析 + H:此时已经有多个操作符,需要相互比较优先级来确定相互的顺序,此时其前一个操作符为(,括号里的优先级是最高的,故当前操作符优先级较高。
解析 C H:复制C
解析 ) H:此时括号里的运算结束,复制按顺序复制括号里的操作符;同时计算式也结束了,复制所有剩余操作符

思考:
如上分析所示,操作符的处理都也是按顺序排的,只是复制时是按照从后到前的顺序处理的(即使是括号中的也是按照这这个规则来排序的)如此,这种存取规则便是栈结构的后进先出的原则,故使用栈结构来存储操作符是非常合适的,请看下面一个例子:



解析过程与上面的例子是一样的,只是这里将操作符装入到了栈中,这里就不做过多赘述了。
由以上可看出中缀表达式转后缀表达式的规律:




源码:
1,数组栈(父类StackModel请参考用数组模拟栈结构,实现分隔符"{}"、"()"、"[]"的左右匹配检查
package net.hncu.d0805;import net.hncu.d0729.StackModel;public class StackArray extends StackModel{//继承自StackModelpublic StackArray(int length) {super(length);}public int size() {//System.out.println(top+1+"here");return top+1;}public Object peekN(int n) {//获取栈中的某一元素(仅仅为了显示栈的状态而存在,不对栈进行任何操作)//System.out.println(a[n]);return a[n];}public void displayStack(String s) {//显示栈的状态System.out.print(s);System.out.print(" Stack (bottom-->top): ");for(int j=0;j<size();j++) {System.out.print(peekN(j)+" ");}System.out.println();}}

2,转换
package net.hncu.d0805;public class InToPost {//定义栈private StackArray stack;//输入的表达式private String input;//输出的表达式private String output;/** * 构造函数,初始化输入的表达式、栈、输出的表达式为空 * @param input */public InToPost(String input) {this.input = input;stack = new StackArray(input.length());output = "";}/** * 解析表达式 * @return */public String doTrans() {//逐个遍历输入的表达式字符for (int i = 0; i < input.length(); i++) {char ch = input.charAt(i);stack.displayStack("Get " + ch + " ");switch (ch) {case '+':case '-':gotOper(ch, 1);//调用优先级为1的函数break;case '*':case '/':gotOper(ch, 2);//调用优先级为2的函数break;case '('://直接压入栈stack.push(ch);break;case ')':gotParen(ch);//遇到)处理括号中的操作符break;default://默认(即数字)直接写入output = output + ch;break;}}while (!stack.isEmpty()) {//遍历写入栈中的操作符stack.displayStack("out ");output = output + stack.pop();}stack.displayStack("End  ");return output;//返回后缀表达式}/** * 当遇到')'处理括号中的 * @param ch */private void gotParen(char ch) {// TODO Auto-generated method stubwhile (!stack.isEmpty()) {char chx = (char) stack.pop();//逐一弹栈写入输出,直到遇到'('if (chx == '(') {break;} else {output += chx;//将括号中的操作符写入}}}/** * '()'匹配处理优先级 * @param ch * @param i */private void gotOper(char ch, int i) {// TODO Auto-generated method stubwhile (!stack.isEmpty()) {//遍历弹出栈顶的操作符(栈顶的优先是最高的)char opTop = (char) stack.pop();if (opTop == '(') {//若入到“(”重新将其压入栈stack.push('(');break;} else {int k;if (opTop == '+' || opTop == '-') {//给弹出栈的操作符定义优先级k = 1;} else {k = 2;}if (k < i) {//比较优先级,如果栈顶的优先级较小将其重新压栈stack.push(opTop);break;} else {output += opTop;//若栈顶优先级较高将栈顶写入}}}stack.push(ch);//将该操作符压栈}}

实现第二步(计算后缀表达式)
计算后缀表达式相对来说就容易了,我们要知道的是,计算后缀表达式时总是从后缀表达式的第一个操作符开始进行计算的,由此每当遇到操作符我们就可以计算该操作符的前两个数,然后将结果放在该操作符的位置。
从计算机的角度来看的话,计算机需要遍历该后缀表达式,遇到数字时还不能进行计算,所以要保存数字,当遇到第一个操作符时就可以计算以保存数字序列的最后两位,计算完后有需要将结果保存到已知数字序列的末尾。以此类推。可能细心的人会发现,对数字的存取过程又是一个栈结构规则。

源码:
1,计算后缀表达式
package net.hncu.d0805;/** * 解析后缀表达式并进行计算 * @author Administrator * */public class ParsePost {private StackArray stack;//定义一个用来装数字的栈private String input;//输入需要处理的后缀表达式字符串public ParsePost(String input) {this.input = input;stack = new StackArray(input.length());}/** * 处理过程 * @return */public int doParse() {int resulet = 0;char ch = '\u0000';for (int i = 0; i < input.length(); i++) {//遍历要处理的后缀表达式ch = input.charAt(i);stack.displayStack("Get: " + ch + " ");if ('0' <= ch && '9' >= ch)//如果遇到数字,压入栈stack.push((int) (ch - '0'));else {//其他(即计算符号)弹出栈顶两项进行计算int num1 = (int) stack.pop();int num2 = (int) stack.pop();switch (ch) {case '+':resulet = num2 + num1;break;case '-':resulet = num2 - num1;break;case '*':resulet = num2 * num1;break;case '/':resulet = num2 / num1;break;default:resulet = 0;}stack.push(resulet);//将计算结果再次压入栈}}return (int) stack.pop();//循环结束后,存在在栈中的就是最总结果}}
2,测试
package net.hncu.d0805;import java.util.Scanner;public class StackTest {public static void main(String[] args) {String output,input;Scanner sc =new Scanner(System.in);while(true) {System.out.println("Enter Infix Expression:");input=sc.nextLine();if("over".equals(input)) break;InToPost theTrans = new InToPost(input);output = theTrans.doTrans();System.out.println("Post Expression is: "+output+'\n');int result = new ParsePost(output).doParse();System.out.println("Result is: "+result+'\n');}}}

结果图:
1,中缀转后缀

2,计算后缀表达式

阅读全文
1 0
原创粉丝点击