前置知识:链表、队列、栈、波兰表达式、中缀表达式和后缀表达式

运行:

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));
    }
}

结果:

java 构建表达式树 java实现表达式计算_java

代码细节↓

第一步接收输入,拆分字符串,将运算符和数字分离,存储到一个链表中

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));
    }
}

输出

java 构建表达式树 java实现表达式计算_java 构建表达式树_02

第二步将中缀表达式转换成后缀表达式,后缀表达式我后面会出一篇文章详解,这里就先请移步百度

//后缀表达式转换
    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));
    }
}

输出

java 构建表达式树 java实现表达式计算_后缀表达式_03

将链表转换为队列,后面计算需要

//链表转队列
    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);
    }
}

输出

java 构建表达式树 java实现表达式计算_java_04

 

一步到位

//执行计算
    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);
    }
}

大功告成,去试试吧!