逆波兰计算器

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值