逆波兰计算器
2017-03-15 18:25
141 查看
1. 中缀表达式
我们在数学中学到的表达式被称为中缀表达式,操作符号在操作数中间,比如 2 + 3 * (5 - 1)。对人类而言,这种表达方式显而易见,求值也很直接,先算乘除再算加减,先算括号内再算括号外。然而,这个表达式对于计算机而言却很费解。你可能会有疑问:这有什么难理解的嘛,在JavaScript、Python或者Ruby,甚至是Java里面都可以通过eval_r('2 + 3 * (5 - 1)')来计算这个表达式。当然,这里的计算机并不是指现而今强大的计算机和高级编程语言,而是指上个世纪中页还处于发展初期的计算机。
2. 后缀表达式
后缀表达式也称为逆波兰式(Reverse Polish Notation, RPN),更加广为人知一些,和前缀表达式刚好相反,是将操作符号放置于操作数之后,比如2 + 3 * (5 - 1)用逆波兰式来表示则是:2 3 5 1 - * +。
3.中缀表达式到后缀表达式的转换
因为通过后缀表达式来进行计算只需要一个栈即可,从硬件和软件上实现都是极为便利的,因此逆波兰式在计算机领域的应用更加广泛,因此将中缀表达式转换为逆波兰式非常重要。使用栈就可以将中缀表达式转换成逆波兰式,转换过程如下:
从左往右遍历中缀表达式中的每个数字和符号,弱是数字就输出,成为逆波兰式的一部分; 如果是右括号,或者是其他符号并且比当前栈顶符号的优先级低,则栈顶元素依次出栈并输出; 然后将当前符号进栈,重复以上操作直到结束。
4.逆波兰表达式的计算
逆波兰式的计算是从左往右依次读取,当读到操作符时,将之前的两个操作数做计算,然后替换这两个操作数和操作符,接着读取,重复此步骤。对于这个表达式,读到5 1 -,得到4,然后读取乘号,取出前面的3和上一步的计算结果4,并计算,到12,接着读取加号+,计算2 12 +得到14,计算结束。上面这个步骤可以很容易的用栈来实现:
从左往右依次读取表达式,如果是数字则将该数字压栈,如果是符号,则将之前的两个数字出栈,做计算后,将计算结果压栈,直到表达式读取结束。栈中剩下的一个数就是计算结果。
召唤代码~
主函数:import java.util.Scanner; import java.util.Stack; import java.util.ArrayList; public class RealCalc { static double op1=0, op2=0; static Stack<Object> calcStack = new Stack<>(); //用于表达式转换和计算 static ArrayList<String> exp = new ArrayList<>(); //用于存储逆波兰表达式 public static void main(String[] args) { Scanner in = new Scanner(System.in); double result = 0; String expression = in.nextLine(); String e = expression; tranfser(expression); // printExp(); //调试用到的函数 result = compute(); if(String.valueOf(result).equals("Infinity")) System.out.println("The divisor cannot be zero !"); else System.out.println(e+" = "+result); } }
函数一:中缀表达式转换成逆波兰表达式
//实质就是对字符串的一系列操作,把字符串转换成想要的形式 public static void tranfser(String expression) { char[] arr = expression.toCharArray(); for(int i=0; i < arr.length; i++) { if(isNum(arr[i]) || (i==0&&arr[i]=='-')) { /* *这部分代码的作用是提取输入的算术表达式(中缀表达式)中的 *有效数字,包括整数.浮点数和负数。 */ int index=0; for(index=i+1; index<arr.length; index++) { if(isOperator(arr[index]) || arr[index]=='(' || arr[index]==')') break; } exp.add(String.valueOf(arr, i, index-i)); i = index-1; //减 1 是因为 for 循环里 i 还会自增 1 } else if(isOperator(arr[i])) { if(calcStack.empty()) calcStack.push(arr[i]); else { if(operatorPriority(arr[i]) >= operatorPriority(calcStack.peek())) calcStack.push(arr[i]); else { for(int j=1; j<= calcStack.size(); j++) exp.add(calcStack.pop().toString()); calcStack.push(arr[i]); } } } else if(arr[i] == '(') { calcStack.push(arr[i]); if(arr[i+1] == '-') { int index=0; for(index=i+2; index<arr.length; index++) { if(isOperator(arr[index]) || arr[index]==')') break; } exp.add( String.valueOf(arr, i+1, index-(i+1)) ); //i-1 是因为不包括左括号 i = index-1; //减 1 是因为 for 循环里 i 还会自增 1 } } else if(arr[i] == ')') { int len = calcStack.size(); for(int k=1; k<=len ; k++) { if(calcStack.peek().toString().equals("(")) { calcStack.pop(); break; } exp.add(calcStack.pop().toString()); } } } if(!calcStack.empty()) { int len = calcStack.size(); for(int i=0; i< len; i++) exp.add(calcStack.pop().toString()); } }
函数二:计算逆波兰表达式
public static double compute() { double temp = 0; for(String atom : exp) { if(isNum(atom)) calcStack.push(atom); else if(isOperator(atom)) { op1 = Double.parseDouble(calcStack.pop().toString()); op2 = Double.parseDouble(calcStack.pop().toString()); //JDK7的新特性可以直接对字符串字面量switch()-case switch(atom) { case "+": temp = op2+op1;break; case "-": temp = op2-op1;break; case "*": temp = op2*op1;break; case "/": temp = op2/op1;break; } calcStack.push(temp); } } return Double.parseDouble(calcStack.pop().toString()); }
其它的辅助函数:
//判断字符是否是阿拉伯数字 public static boolean isNum(Object o) { Character c = (Character)o; return (c>='0' && c<='9')?true:false; }//判断字符串是否是有效数字 public static boolean isNum(String s) { /* *这个函数的思路还是蛮有意思的(可能对我这个新手来说,比较新奇), *搜索了很多如何判断字符串是有效的数字,大多数都是利用正则表达式(可是我还没学到~尴尬)。 *经过群友的点拨,想到可以通过捕捉NumberFormatException来判断! */ try{ Double.parseDouble(s); return true; }catch(NumberFormatException e){ return false; } } //判断字符是否是运算符 public static boolean isOperator(Object o) { Character c = (Character)o; String s = "+-*/"; if(s.indexOf(c) != -1) return true; return false; }//判断字符串是否是运算符 public static boolean isOperator(String s) { String operator = "+-*/"; if(operator.indexOf(s)!=-1) return true; return false; } //设置运算符的优先级 public static int operatorPriority(Object o) { Character c = (Character)o; switch(c) { case '+': return 1; case '-': return 1; case '*': return 2; case '/': return 2; default: return 0; } } //测试函数,打印exp(最后结果不对,就打印一下转换得到的逆波兰表达式,看看是否转换时就已经有问题了) public static void printExp() { for(String s : exp) System.out.println(s); }
相关文章推荐
- 波兰式计算器程序(C++)
- 逆波兰计算器--中缀表达式转后缀表达式
- 栈和队列5|逆波兰计算器 - 数据结构和算法27
- 波兰式计算器程序(C++)
- 栈和队列5|逆波兰计算器 - 数据结构和算法27
- 计算器 | 逆波兰表达式
- 怎么实现Linux下的逆波兰计算器dc?
- 逆波兰计算器改进版
- HDU1237 简单的计算器 【堆】+【逆波兰式】
- 栈与队列(二)之逆波兰(RPN)计算器
- 逆波兰表达式思想下的计算器
- 逆波兰计算器
- c# 逆波兰式实现计算器
- C程序设计语言逆波兰式计算器学习心得
- 逆波兰实现计算器
- 基于逆波兰RPN算法的计算器实现
- 栈之逆波兰计算器
- 逆波兰计算器(存疑)
- 计算器(逆波兰运算表达式)
- 计算器之逆波兰算法