您的位置:首页 > 其它

逆波兰计算器

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: