【Java数据结构与算法】 应用篇(2):中缀表达式转后缀表达式、带小括号四则表达式运算

大家好,我是皮皮猫吖!

每文一言:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力,却得不到结果的日子,我们把它叫做扎根。

本篇文章:

主要是关于java数据结构与算法的一些应用:中缀表达式与后缀表达式之间的相互转换、带小括号四则表达式的计算。

正文如下:

一、前缀、中缀、后缀表达式(逆波兰表达式)

1)前缀表达式(波兰表达式)

① 前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前

举例说明:

(3+4)×5-6 对应的前缀表达式就是 - × + 3 4 5 6

③ 前缀表达式的计算机求值过程:

前缀表达式从右至左扫描表达式,当遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。

④ 例如:(3+4)×5-6 对应的前缀表达式就是:- × + 3 4 5 6;针对前缀表达式求值步骤如下:

  • 右至左扫描,将6、5、4、3依次压入堆栈
  • 遇到+运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算出3+4的值,得7,再将7入栈
  • 接下来是 × 运算符,因此弹出7和5,计算出7×5=35,将35入栈
  • 最后是 - 运算符,计算出35-6的值,即29,由此得出最终结果
2)中缀表达式

① 中缀表达式就是常见的运算表达式,如(3+4)×5-6

② 中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作(前面我们实现的应用【简单四则运算】就能看的这个问题),因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式.)

3)后缀表达式(逆波兰表达式)

① 后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后

在这里插入图片描述
② 举例:(3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 –

③ 后缀表达式的计算机求值过程:

从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。

④ 例子: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 -,针对后缀表达式求值步骤如下:

  • 从左至右扫描,将3和4压入堆栈;
  • 遇到 + 运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
  • 将5入栈;
  • 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
  • 将6入栈;
  • 最后是 - 运算符,计算出35-6的值,即29,由此得出最终结果
4)实现一个逆波兰计算器:
① 使用后缀表达式进行运算
package com.data.structure.study4.stack;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

/**
 * 后缀表达式计算
 * @author imppm
 * @create 2021-03-11-20:06
 */
public class Stack4ReversePoland2 {
    public static void main(String[] args) {
        //定义逆波兰表达式
        String str = "3 4 + 5 * 6 - ";

        //获取到逆波兰表达式中字符的集合
        List<String> list = getListString(str);

        //获取到逆波兰表达式的值
        int compute = getCompute(list);

        System.out.println(str + " = "+compute);

    }

    //判断当前字符串是否为数字字符串
    private static boolean isDigit(String str){
        return str.charAt(0) >= '0' && str.charAt(0) <= '9';
    }

    //使用运算符进行计算
    private static int compute(int a, int b, String str){
        switch (str){
            case "+":
                return a + b;
            case "-":
                return a -  b;
            case "*":
                return a * b;
            case "/":
                return a / b;
            default:
                throw new RuntimeException("无法识别该运算符!");
        }
    }


    //获取到逆波兰表达式计算的值
    private static int getCompute(List<String> list){
        //定义迭代器:遍历list集合【逆波兰表达式中字符的集合】
        Iterator iterator = list.iterator();
        //创建一个栈
        Stack<Integer> stack = new Stack<>();

        //遍历栈
        while (iterator.hasNext()){

            //依次获取集合中存储的字符串
            String str = iterator.next().toString();
            //判断该字符串是否为数字字符串
            if(isDigit(str)){
                //向栈中添加数字
                stack.add(str.charAt(0) - '0');
            }else{
                //如果集合中存储的该字符为运算字符
                //两个数据出栈
                //进行该运算符运算
                int num2 = stack.pop();
                int num1 = stack.pop();
                int num = compute(num1, num2, str);

                //将计算得到的结果入栈
                stack.push(num);
            }

        }
        //最后将栈最后的结果返回
        return stack.pop();
    }

    //根据后缀表达式字符串,转换为存储后缀字符的集合
    private static List<String> getListString(String str){
        //截取获取字符串
        String[] ss = str.split(" ");

        //创建一个lis t集合
        List<String> list = new ArrayList<>();

        //遍历字符串
        for (String s:ss){
            //将遍历的字符串,添加到集合中
            list.add(s);
        }
        //返回集合
        return list;
    }
}

二、中缀表达式转换为后缀表达式的步骤

1)中缀表达式转后缀表达式的过程,分析如下:

(1) 初始化两个栈:运算符栈s1和储存中间结果的栈s2;

(2) 从左至右扫描中缀表达式;

(3) 遇到操作数时,将其压s2;

(4) 遇到运算符时,比较其与s1栈顶运算符的优先级:

  • 1)如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
  • 2)否则,若优先级比栈顶运算符的高,也将运算符压入s1;
  • 3)否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较

(5) 遇到括号时:

  • 1)如果是左括号“(”,则直接压入s1
  • 2)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃

(6) 重复步骤2至5,直到表达式的最右边

(7) 将s1中剩余的运算符依次弹出并压入s2

(8) 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

在这里插入图片描述

2)中缀表达式转后缀表达式的模板代码:
//中缀表达式转换为后缀表达式:传入一个中缀表达式的集合,返回一个后缀表达式的集合
    public static List<String> turnInfixToPostfix(List<String> list){
        //创建一个后缀表达式的集合:用来存储得到的后缀表达式
        List<String> postfixList = new ArrayList<>();

        //运算符栈
        Stack<String> operatorStack = new Stack<>();

        //遍历中缀表达式的集合
        for (String ss : list){

            //是数字串:直接添加到后缀表达式集合中
            if(ss.matches("\\d+")){
                postfixList.add(ss+"");
            }

            //如果直接是左括号,直接压入到运算符栈中
            else if("(".equals(ss)){
                operatorStack.push(ss);
            }
            //如果是右括号,进行计算,直到遇到小括号
            else if(")".equals(ss)){

                //如果是右括号,遍历运算符栈,直到遇到左括号
                while (!"(".equals(operatorStack.peek())){
                    postfixList.add(operatorStack.pop());
                }

                //将最左边的左括号进行出栈
                operatorStack.pop();
            }else{

                //左括号的运算符,优先级为0,为最小值
                //如果运算符栈栈顶优先级大于当前运算符优先级,栈顶运算符添加到后缀表达式集合中
                while (operatorStack.size()!=0 && operator.getOperatorPriority(operatorStack.peek()) >= operator.getOperatorPriority(ss)){
                    postfixList.add(operatorStack.pop());
                }

                //将当前运算字符串压入到栈中
                operatorStack.push(ss);
            }
        }

        //遍历运算符栈剩余的运算符
        while (operatorStack.size()!=0){
            postfixList.add(operatorStack.pop());
        }

        //返回后缀表达式集合
        return postfixList;
    }

class operator{
    //加法运算符优先级
    private static final int SUM_PRIORITY = 1;
    //减法运算符优先级
    private static final int SUB_PRIORITY = 2;
    //乘法运算符优先级
    private static final int MUL_PRIORITY = 2;
    //除法运算符优先级
    private static final int DIV_PRIORITY = 2;


    //获取运算符的优先级
    public static int getOperatorPriority(String data){
        int result = 0;
        switch (data){
            case "+":
                result = SUM_PRIORITY;
                break;
            case "-":
                result = SUB_PRIORITY;
                break;
            case "*":
                result = MUL_PRIORITY;
                break;
            case "/":
                result = DIV_PRIORITY;
                break;
            default:
                break;
        }
        return result;
    }
}

三、应用:带小括号的四则表达式运算(中缀转后缀表达式方法)【注:这个博客(栈实现带符号的四则表达式计算)有其他方法】

1)方法一:使用中缀表达式转后缀表达式的方法【略微复杂】:
package com.data.structure.study4.stack;

import java.util.Iterator;
import java.util.Stack;

/**
 * @author imppm
 * @create 2021-03-11-23:10
 */
public class Stack5InfixPostfix1 {
    public static void main(String[] args) {
        Stack5 stack5 = new Stack5();

        //stack4.compute("7*2*2-5+1-5+3-4");//18
        //stack4.compute("7*2*2-2*1-5*3-4");//5
        //stack4.compute("100-(2-3)*2");//104

//        Stack<String> compute = stack5.compute("(1+2)-3*4+2*(1+2)");//3-12+6=-3 //  1 2 + 3 4 * - 2 1 2 + * +

//        Stack<String> compute = stack5.compute("1+((2+3)*4)-5");
//        Stack<String> compute = stack5.compute("7*2*2-5*(2-1)*3-4");//28-5-15-4+1=5 28-5-5-3-4+1

        Stack<String> compute = stack5.compute("123+((2+3)*4)-5");//28-5-15-4+1=5 28-5-5-3-4+1

        //获取stack的iterator
        Iterator it = compute.iterator();

        //遍历栈:可以从前往后遍历
        while (it.hasNext()){
            System.out.print(it.next()+" ");
        }

        //使用后续存储的栈,得到最后计算的结果
        String s = stack5.postfixCompute(compute);
        System.out.println(" = "+s);

    }
}

class Stack5 {
    //加法运算符的优先级
    private final int sumPriority = 0;

    //减法运算符的优先级
    private final int subPriority = 0;

    //乘法运算符的优先级
    private final int mulPriority = 1;

    //除法运算符的优先级
    private final int divPriority = 1;

    //左括号的优先级
    private final int leftParenthesisPriority = 2;

    //右括号的优先级
    private final int rightParenthesisPriority = -1;

    //运算符栈
    private char[] operatorStack = new char[10];

    //运算符栈栈顶指针
    private int operatorTop = -1;

    //用来判断是否输入的式多位数
    private boolean flag = false;


    //中缀表达式转换为后缀表达式
    public Stack<String> compute(String str){

        //初始化一个栈,用来存储后缀表达式
        Stack<String> stack = new Stack<>();

        //遍历当前字符
        for (int i = 0; i < str.length(); i++){
            //获取字符
            char ch = str.charAt(i);

            //是否为数字
            if(isDigit(ch)){
                if(flag){
                    //多位数字判断
                    String data = stack.pop();
                    int int_data = Integer.parseInt(data);
                    int_data = int_data * 10 + (ch - '0');
                    stack.push(int_data+"");
                }else{
                    //数字直接入栈
                    stack.push((ch-'0')+"");


                }
                //当前数字连续
                flag = true;

            }
            //是否为运算符
            else{
                //当前数字不连续
                flag = false;

                //判断运算符栈是否为空:如果运算符栈中没有运算符,直接将该运算符压入栈中
                if(operatorTop==-1){
                    //将该运算符压入栈中
                    operatorStack[++operatorTop] = ch;
                   continue;
                }

                //获取运算符栈顶元素
                char operatorStackTopData = getOperatorStackTopData();

                //如果运算符栈顶元素优先级小于当前运算符优先级,直接入栈
                if(readOperatorPriority(operatorStackTopData) < readOperatorPriority(ch)){
                    operatorStack[++operatorTop] = ch;
                }

                //栈顶运算符的优先级大于或等于当前运算符的优先级
                else{

                    while(true) {

                        //如果栈顶运算符的优先级小于当前运算符的优先级,打破该循环
                        if(readOperatorPriority(getOperatorStackTopData()) < readOperatorPriority(ch)){
                            operatorStack[++operatorTop] = ch;
                            break;
                        }

                        //如果当前栈顶元素为'(',后面的运算符不为')'
                        if (getOperatorStackTopData() == '(' && ch != ')') {
                            //将ch元素,压入栈中
                            operatorStack[++operatorTop] = ch;
                            break;
                        }
                        //如果当前栈顶元素为'(',后面的运算符为')',栈顶指针-1,')'不入栈
                        else if (getOperatorStackTopData() == '(') {
                            operatorTop--;
                            break;
                        }

                        //获取到运算符
                        char operator = operatorStack[operatorTop--];


                        //将运算符压入到后缀表达式栈中
                        stack.push(operator+"");

                        //如果运算符栈为空:将当前运算符压入到栈中
                        if(operatorTop==-1){
                            operatorStack[++operatorTop] = ch;
                            break;
                        }
                    }
                }
            }
        }

        while (operatorTop!=-1){
            //获取到运算符
            char operator = operatorStack[operatorTop--];

            //将运算符压入到后缀表达式栈中
            stack.push(operator + "");
        }
        return stack;
    }

    //是否为数字
    private boolean isDigit(char data){
        return data >= '0' && data <= '9';
    }

    //获取运算符栈栈顶元素
    private char getOperatorStackTopData() {
        return operatorStack[operatorTop];
    }

    //读取当前运算符的优先级
    private int readOperatorPriority(char data) {
        switch (data) {
            case '+':
                return sumPriority;
            case '-':
                return subPriority;
            case '*':
                return mulPriority;
            case '/':
                return divPriority;
            case '(':
                return leftParenthesisPriority;
            case ')':
                return rightParenthesisPriority;
            default:
                throw new RuntimeException("运算符输入有误!");
        }
    }

    //两个数值进行计算
    private static int compute(int a, int b, String str){
        switch (str){
            case "+":
                return a + b;
            case "-":
                return a -  b;
            case "*":
                return a * b;
            case "/":
                return a / b;
            default:
                throw new RuntimeException("无法识别该运算符!");
        }
    }

    //后缀表达式的计算,传入的是后缀表达式栈
    public String postfixCompute(Stack stack){
        //获取集合栈的迭代器
        Iterator it = stack.iterator();

        //存储数据的栈:用来存储数,
        Stack<String> stack1 = new Stack<>();

        //遍历存储数据的栈
        while (it.hasNext()){
            //拿到栈中的数据
            String data = it.next().toString();
            //该数据是否为数字
            if(data.matches("\\d+")){
                //如果是数字串存储到新的栈中
                stack1.push(data);
            }else{
                //如果是运算符,进行计算
                //获取数据1
                int num2 = Integer.parseInt(stack1.pop());
                //获取数据2
                int num1 = Integer.parseInt(stack1.pop());
                //使用运算符进行计算
                int num = compute(num1, num2, data);
                //将运算得到的结果压入栈中
                stack1.push(num+"");
            }
        }
        //将最后的结果压入栈中
        return stack1.pop();

    }

}


2)方法二:使用中缀表达式转后缀表达式的方法【较简单】:
package com.data.structure.study4.stack;


import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 *
 * 中缀字符串进行四则运算:
 *      1.先将中缀表达式转换成后缀表达式
 *      2.计算后缀表达式
 * @author imppm
 * @create 2021-03-12-18:02
 */
public class Stack5InfixPostfix2 {
    public static void main(String[] args) {
        //中缀表达式
        //注意表达式中的*号,就是乘号
        String expression = "123+((2+3)*4)-5";

        //中缀字符串转换为中缀集合:方便遍历
        List<String> list = stringToList(expression);

        //获取到后缀表达式的集合
        List<String> list1 = turnInfixToPostfix(list);

        //计算后缀表达式
        String s = computePostfixExpression(list1);


        //遍历中缀表达式集合
        System.out.println(expression + " = "+s);
    }

    //计算后缀表达式集合
    public static String computePostfixExpression(List<String> list){
        //创建一个数据栈:存储数据
        Stack<String> stack = new Stack<>();

        //遍历后缀表达式集合
        for (String ss :list){
            //如果是一个或多个数字的话
            if(ss.matches("\\d+")){
                //直接压入栈中
                stack.push(ss);
            }
            //该字符串为运算符
            else{
                //栈顶数据
                int num2 = Integer.parseInt(stack.pop());
                //次栈顶数据
                int num1 = Integer.parseInt(stack.pop());

                //两个数据使用运算符进行计算
                int num = computeByOperator(num1, num2, ss);

                //将计算的结果压入到数据栈中
                stack.push(num+"");
            }
        }
        return stack.peek();
    }

    //计算两个数据的和
    private static int computeByOperator(int a, int b, String operator){
        switch (operator){
            case "+":
                return a + b;
            case "-":
                return a - b;
            case "*":
                return a * b;
            case "/":
                return a / b;
            default:
                throw new RuntimeException("运算符输入有误!");
        }
    }

    //中缀表达式转换为后缀表达式
    public static List<String> turnInfixToPostfix(List<String> list){
        //创建一个后缀表达式的集合:用来存储得到的后缀表达式
        List<String> postfixList = new ArrayList<>();

        //运算符栈
        Stack<String> operatorStack = new Stack<>();

        //遍历中缀表达式的集合
        for (String ss : list){

            //是数字串:直接添加到后缀表达式集合中
            if(ss.matches("\\d+")){
                postfixList.add(ss+"");
            }

            //如果直接是左括号,直接压入到运算符栈中
            else if("(".equals(ss)){
                operatorStack.push(ss);
            }
            //如果是右括号,进行计算,直到遇到小括号
            else if(")".equals(ss)){

                //如果是右括号,遍历运算符栈,直到遇到左括号
                while (!"(".equals(operatorStack.peek())){
                    postfixList.add(operatorStack.pop());
                }

                //将最左边的左括号进行出栈
                operatorStack.pop();
            }else{

                //左括号的运算符,优先级为0,为最小值
                //如果运算符栈栈顶优先级大于当前运算符优先级,栈顶运算符添加到后缀表达式集合中
                while (operatorStack.size()!=0 && operator.getOperatorPriority(operatorStack.peek()) >= operator.getOperatorPriority(ss)){
                    postfixList.add(operatorStack.pop());
                }

                //将当前运算字符串压入到栈中
                operatorStack.push(ss);
            }
        }

        //遍历运算符栈剩余的运算符
        while (operatorStack.size()!=0){
            postfixList.add(operatorStack.pop());
        }

        //返回后缀表达式集合
        return postfixList;
    }


    //字符串转换为list集合
    public static List<String> stringToList(String str){
        //创建一个list数组
        List<String> list = new ArrayList<>();
        //用来判断是否连续输入数字
        boolean flag = false;

        //遍历当前字符串
        for (int i = 0; i < str.length(); i++){
            char ch = str.charAt(i);
            //如果是数字
                if(isDigit(ch)){
                    //判断是否出现连续数字情况
                    if(flag){
                        //读取最后一个数字
                        int data = Integer.parseInt(list.get(list.size()-1));
                        //该数字*10+下一位数字
                        data = data * 10 + ch - '0';
                        //更改原来数字的值:更改为连续数字的值
                        list.set(list.size()-1, data+"");
                        //跳过这次循环
                        continue;
                    }
                    else{
                        //直接放到集合中
                        list.add(ch+"");
                    }
                    //连续标志为设置为true
                    flag = true;
                }
                //该字符为运算符
                else{
                    //数字连续标志位位false
                    flag = false;
                    //直接将运算符添加到集合中
                    list.add(ch+"");
                }
        }
        //返回list集合
        return list;
    }

    //判断该字符是否为数字字符
    private static boolean isDigit(char ch){
        return ch >= '0' && ch <= '9';
    }

}


class operator{
    //加法运算符优先级
    private static final int SUM_PRIORITY = 1;
    //减法运算符优先级
    private static final int SUB_PRIORITY = 2;
    //乘法运算符优先级
    private static final int MUL_PRIORITY = 2;
    //除法运算符优先级
    private static final int DIV_PRIORITY = 2;


    //获取运算符的优先级
    public static int getOperatorPriority(String data){
        int result = 0;
        switch (data){
            case "+":
                result = SUM_PRIORITY;
                break;
            case "-":
                result = SUB_PRIORITY;
                break;
            case "*":
                result = MUL_PRIORITY;
                break;
            case "/":
                result = DIV_PRIORITY;
                break;
            default:
                break;
        }
        return result;
    }
}

希望本篇文章对大家有所帮助,后续会继续分享java数据结构与算法相关学习知识…

如果文章内容有错误的地方,请在留言处留下你的见解,方便大家共同学习。谢谢!

如有侵权或其他任何问题请联系:QQ1370922071,本文主要用于学习交流,转载请声明!

作者:皮皮猫吖


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值