用Java實現一個表達式計算器

前置知識:鏈表、隊列、棧、波蘭表達式、中綴表達式和後綴表達式

運行:

public class Test
{
    public static void main(String[] args) throws Exception {
        RPN2 rpn = new RPN2();
        String str = "(5*(4+2)+10)/(4+2*2)";
        System.out.println(str + "=" + rpn.execute(str));
    }
}

結果:

代碼細節↓

第一步接收輸入,拆分字符串,將運算符和數字分離,存儲到一個鏈表中

public class RPN2
{
    //表達式輸入,字符串分解;分離操作符和操作數
    public ArrayList<Object> strExecute(String str) throws Exception
    {
        //將表達式的每個字符用數組鏈表存儲
        ArrayList<Object> result = new ArrayList<>();
        str = str.trim();

        for(int i = 0; i < str.length(); i ++)
        {
            String op = "";

            //若這個字符爲數字,則繼續遍歷下一個元素,直到元素爲非數字
            while(str.charAt(i) >= 48 && str.charAt(i) <= 57 || str.charAt(i) == '.')
            {
                //若爲連續數字,則拼接起來
                op += str.charAt(i);
                i ++;
                if(i >= str.length())
                    break;
            }

            //若字符串不等於空,則一定爲數字字符串
            if(!op.equals("") && op.length() > 0 && !op.equals(" "))
                result.add(Double.valueOf(op)); //將數字字符轉換爲Double類型(方便後續計算)添加至鏈表

            if(i >= str.length())
                break;

            if(str.charAt(i) == ' ')    //若爲空格,則跳到下一次循環
                continue;

            //不爲數字(即操作符)
            if(str.charAt(i) < 48 || str.charAt(i) > 57)
            {
                char op2 = str.charAt(i);
                if(op2 == '+' || op2 == '-' || op2 == '*' || op2 == '/' || op2 == '(' || op2 == ')')
                    result.add(str.charAt(i) + ""); //操作符轉成字符串添加到鏈表
                else throw new Exception("不合法的運算符:" + "\"" + op2 + "\"");
            }
        }

        return result;  //返回表達式鏈表
    }
    // 把表達式轉成字符串,方便調試顯示
    public String toString(ArrayList<Object> expr)
    {
        String result = "";
        for(Object item: expr)
        {
            result += item.toString() + "  ";
        }
        return result;
    }

單元測試:

將每個字符單元拆分出來,也就是中綴表達式

public class Test
{
    public static void main(String[] args) throws Exception {
        RPN2 rpn = new RPN2();
        String str = "(5*(4+2)+10)/(4+2*2)";
        ArrayList<Object> e = rpn.strExecute(str);
        System.out.println("拆分後:" + e);
        System.out.println("中綴表達式爲:" + rpn.toString(e));
    }
}

輸出

第二步將中綴表達式轉換成後綴表達式,後綴表達式我後面會出一篇文章詳解,這裏就先請移步百度

    //後綴表達式轉換
    public ArrayList<Object> convert(ArrayList<Object> src)
    {
        ArrayList<Object> dst = new ArrayList<>();
        Stack stack = new Stack();

        for(Object item : src)
        {
            if(item instanceof Integer || item instanceof Double) //操作數
            {
                dst.add(item);
            }
            else if(item instanceof String) //操作符
            {
                String op = ((String) item).trim();
                int p1 = priority(op); //操作符優先級

                if(op.equals("("))
                {
                    stack.push(op);
                }
                else if(op.equals(")"))
                {
                    //直到匹配到左括號
                    while(stack.size() > 0)
                    {
                        String op2 = (String) stack.pop();
                        if(op2.equals("(")) break;
                        dst.add(op2);
                    }
                }
                else
                {
                    //從stack彈出操作符,一直到遇到左括號,或者遇到比自己優先級低的符號
                    while(stack.size() > 0)
                    {
                        String op2 = (String) stack.peek(); //僅查看,不彈出
                        int p2 = priority(op2);
                        if(op2.equals("(")) //左括號應留在棧內
                            break;
                        if(p2 < p1) //遇到比自己優先級低的符號
                            break;

                        dst.add(op2);
                        stack.pop();
                    }
                    stack.push(op);
                }
            }
        }

        while (stack.size() > 0)
        {
            dst.add(stack.pop());
        }

        return dst;
    }

    //運算符優先級
    public int priority(String op)
    {
        if("+ -".indexOf(op) >= 0) return 1;
        if("* /".indexOf(op) >= 0) return 2;
        return 0;
    }

單元測試:

將中綴表達式轉換成了後綴表達式

public class Test
{
    public static void main(String[] args) throws Exception {
        RPN2 rpn = new RPN2();
        String str = "(5*(4+2)+10)/(4+2*2)";
        ArrayList<Object> e = rpn.strExecute(str);
        System.out.println("中綴表達式爲:" + rpn.toString(e));
        ArrayList<Object> e2 = rpn.convert(e);
        System.out.println("後綴表達式爲:" + rpn.toString(e2));
    }
}

輸出

將鏈表轉換爲隊列,後面計算需要

    //鏈表轉隊列
    public Queue<Object> list2Queue(List<Object> list)
    {
        Queue<Object> queue = new ArrayDeque<>();

        Iterator<Object> iterator = list.iterator();
        while(iterator.hasNext())
        {
            queue.add(iterator.next());
        }

        return queue;
    }

最後一步,後綴表達式的計算,返回結果

    //後綴表達式計算
    public Object calculation(Queue<Object> src)
    {
        Stack stack = new Stack(); //用於存放操作數

        while(src.size() > 0)
        {
            //從隊列中彈出一個元素
            Object item = src.poll();
            //若元素爲操作數,則直接入棧
            if(item instanceof Double || item instanceof Integer)
            {
                stack.push(item);
            }
            //若元素爲操作符,則彈出棧內最上層的兩個操作數進行運算,將結果壓棧
            else if(item instanceof String)
            {
                //操作符(+ - * /)
                String op = (String) item;

                Double right = (Double) stack.pop();    //彈出一個數
                Double left = (Double) stack.pop();
                Double sum = 0.0d;  //結果

                if(op.equals("+"))
                    sum = left + right;
                else if(op.equals("-"))
                    sum = left - right;
                else if(op.equals("*"))
                    sum = left * right;
                else if(op.equals("/"))
                    sum = left / right;

                stack.push(sum);    //將計算結果入棧
            }
        }

        return stack.pop(); //最終棧內存儲的是一個計算結果,將之彈出
    }

測試代碼:

import java.util.ArrayList;
import java.util.Queue;

public class Test
{
    public static void main(String[] args) throws Exception {
        RPN2 rpn = new RPN2();
        String str = "(5*(4+2)+10)/(4+2*2)";
        ArrayList<Object> e = rpn.strExecute(str);
        System.out.println("中綴表達式爲:" + rpn.toString(e));
        ArrayList<Object> e2 = rpn.convert(e);
        System.out.println("後綴表達式爲:" + rpn.toString(e2));
        //將後綴表達式的鏈表轉換爲隊列
        Queue<Object> queue = rpn.list2Queue(e2);
        Object result = rpn.calculation(queue);
        System.out.println("結果:" + result);
    }
}

輸出

 

一步到位

    //執行計算
    public Object execute(String expression) throws Exception
    {
        //拆分表達式字符串,將數字和運算符分離
        ArrayList<Object> str = strExecute(expression);
        //將表達式轉換成後綴表達式
        ArrayList<Object> suffixEx = convert(str);
        //鏈表轉換成隊列,運算時需要用到隊列
        Queue<Object> queue = list2Queue(suffixEx);

        //返回運算結果
        return calculation(queue);
    }
}

大功告成,去試試吧!

附上源碼下載地址:https://download.csdn.net/download/qq_41932905/12317704

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章