逆波兰计算器(中缀转后缀)

逆波兰计算器

输入一个逆波兰表达式(后缀表达式),使用栈(Stack),计算其结果

思路如下

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

  1. 从左至右扫描。将3和4压入堆栈;
  2. 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈
  3. 将5入栈
  4. 接下来是X运算符,因此弹出5和7,计算出7X5 = 35,将35入栈
  5. 将6入栈
  6. 最后是-运算符,计算出35-6的值,即25,由此得出最终结果

代码如下:

public  static  int Calculation(ArrayList<String> list){
        //创建一个栈 用来计算结果
        Stack<String> stack = new Stack<>();
        for (String s : list) {
            if(s.matches("\\d+")){//如果数字就直接入栈
                stack.push(s);
            }else{
                //如果是符号 就从栈中 取出栈顶和次顶的数 进行计算,再将结果入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0; //存储计算的结果
                if(s.equals("+")){
                    res = num1+num2;
                }else if(s.equals("-")){
                    res = num1-num2;
                }else if(s.equals("*")){
                    res = num1*num2;
                }else if(s.equals("/")){
                    res = num1/num2;
                }else{
                    throw  new RuntimeException("符号错误");
                }
                //将计算结果再压入栈中
                stack.push(""+res);
            }
        }
        //循环结束后 栈中只剩一个值 那么就是最后的运算结果
        return Integer.parseInt(stack.pop());
    }

中缀表达式转换为后缀表达式

思路如下

  1. 初始化两个栈: 运算符栈s1和储存中间结果的栈s2;
  2. 从左至右扫描中缀表达式;
  3. 遇到操作数时,将其压s2;
  4. 遇到运算符时,比较其与s1栈顶运算符的优先级
    • 如果s1为空,或栈顶运算符为左括号"(",则直接将此运算符入栈;
    • 否则,若优先级比栈顶运算符的高,也将运算符压入s1;
    • 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
  5. 遇到括号时:
    • 如果是左括号"(",则直接压入s1
    • 如果是右括号")",则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃。
  6. 重复步骤2至5,直到表达式的最右边
  7. 将s1中剩余的运算符依次弹出并压入到s2
  8. 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

举例说明

将中缀表达式"1+((2+3)x4)-5"转换为后缀表达式的过程如下
结果为:“1 2 3 + 4 X + 5 -”
在这里插入图片描述

代码如下

package day05;

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

public class PolandNotation {
    public static void main(String[] args) {


        //完成将一个中缀表达式转换成后缀表达式的功能
        //说明
        //1. 1+((2+3)x4)-5 =>转成1 2 3 + 4 x + 5 -
        //2.因为直接对str进行操作,不方便,因此 "1+((2+3)x4)-5" ==>中缀的表达式对于的list
        //3. 即"1+((2+3)x4)-5"  => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
        //4. 将得到的中缀表达式对于的List => 后缀表达式对应的List
        // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] => ArrayList [1,2,3,+,4,*,+,5,-]

        String express = "1+((20+3)*4)-5";
        ArrayList<String> list = toExpressList(express);
        System.out.println("中缀表达式为"+list);
        ArrayList<String> list1 = toSuffixExpressList(list);
        System.out.println("后缀表达式为"+list1);
        int result = Calculation(list1);
        System.out.println(express + "的结果=" + result);

//        //先定义逆波兰表达式
//        //(3+4)x5-6 => 3 4 + 5 x 6 - =>29
//        //为了方便 逆波兰表达式的数字和符号使用空格隔开
//        String suffixExpression = "3 4 + 5 * 6 -";
//
//        //1.我们先将字符串加入ArrayList中 方便我们进行遍历
//        ArrayList<String> list = getStringList(suffixExpression);
//        System.out.println(list);
//
//        //2.遍历ArrayList 计算结果
//        int res = Calculation(list);
//        System.out.println("最后的运算结果为"+res);
    }

    public  static  ArrayList<String> toSuffixExpressList(ArrayList<String> ls){
        //1.定义一个符号栈用来存储符号s1
        Stack<String> s1 = new Stack<>();
        //2.为了方便计算 将存储操作数 定义为ArrayList 如果是栈的话 还有进行逆序 比较麻烦
        ArrayList<String> list = new ArrayList<>();
        //3.遍历中缀表达式的List 根据规则转换为后缀表达式
        for (String item : ls) {
            //4. 如果是操作数的话 就直接加入list中
            if(item.matches("\\d+")){
                list.add(item);
            }else if (item.equals("(")){
                //5.如果是左括号的话 就直接压入s1栈中
                s1.push(item);
            }else if (item.equals(")")){
                //6. 如果是右括号,则依次弹出s1栈顶的运算符,并加入list,直到遇到左括号为止,并将左括号从s1栈中弹出
                while(!s1.peek().equals("(")){
                    list.add(s1.pop());
                }
                //7.并将左括号从s1栈中弹出
                s1.pop();
            }else {
                //8.如果是运算符做一下的处理
                if(s1.isEmpty() || s1.peek().equals("(")){
                    //9.如果s1栈为空 或者s1栈顶的运算符为( 则直接将此运算符入栈
                    s1.push(item);
                }else if( (Operator.getValue(item))>(Operator.getValue(s1.peek())) ){
                    //10. 如果当前运算符的优先级大于s1栈顶的优先级 那么就直接将此运算符入栈
                    s1.push(item);
                }else{
                    //11.当前运算符的优先级小于s1栈顶的优先级 依次从s1栈顶取值直到当前运算符的优先级大于等于栈顶的优先级
                    while (s1.size()>0 && (Operator.getValue(item))<=(Operator.getValue(s1.peek()))){
                        list.add(s1.pop());
                    }
                    //12.循环结束 就代表当前item优先级大于当前栈顶的优先级
                    // 就需要将这item再次压入栈中
                    s1.push(item);
                }
            }
        }
        //12.将s1中剩余的运算符依次加入s2中
        while(s1.size()>0){
            list.add(s1.pop());
        }
        return list;
    }


    //将中缀表达式字符串 转换成相应的中缀表达式list
    public  static  ArrayList<String> toExpressList(String expression) {
        //1.定义一个索引用来扫描字符串
        int i = 0;
        //2.定义一个char变量 用来接收每个字符串中的字符
        char c = ' ';
        //3.定义一个String变量, 用来处理多位数拼接的问题
        String str;
        //4. 创建一个ArrayList 用来接收 转换之后的结果
        ArrayList<String> list = new ArrayList<>();
        //5.进行循环转换
        //5.1 当i小于字符串的时候 一直循环 直到i大于等于字符串结束循环
        while (i < expression.length()) {
            //这里运用的ASCII码进行比较的  小于48 大于57 就是非数字   大于48 小于57 就是数字
            //5.2 如果字符串是非数字的时候 直接添加
            if ((c = expression.charAt(i)) < 48 || (c = expression.charAt(i)) > 57) {
                list.add(c + ""); //5.3将字符添加到list中
                i++; //5.4 更新索引i 进行扫描下一个字符
            } else {  //5.5考虑多位数的问题
                // 5.6 每次拼接完 都要将拼接的字符串 变为""
                str = "";
                //5.7如果i的长度小于 字符串的长度 并且是数字
                while (i < expression.length() && (c = expression.charAt(i)) >= 48 && (c = expression.charAt(i)) <= 57) {
                    str += c;

                    i++;
                }
                //5.8循环结束 那么str就是多位数
                list.add(str);
            }
        }
            return  list;
    }

    public  static ArrayList<String> getStringList(String expression){
        //1. 先将expression 空格进行分割
        String[] split = expression.split(" ");
        //2.创建ArrayList
        ArrayList<String> list = new ArrayList<>();
        //3. 遍历字符数组 加入ArrayList中
        for(String rmp: split){
            list.add(rmp);
        }
        return  list;
    }
    public  static  int Calculation(ArrayList<String> list){
        //创建一个栈 用来计算结果
        Stack<String> stack = new Stack<>();
        for (String s : list) {
            if(s.matches("\\d+")){//如果数字就直接入栈
                stack.push(s);
            }else{
                //如果是符号 就从栈中 取出栈顶和次顶的数 进行计算,再将结果入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0; //存储计算的结果
                if(s.equals("+")){
                    res = num1+num2;
                }else if(s.equals("-")){
                    res = num1-num2;
                }else if(s.equals("*")){
                    res = num1*num2;
                }else if(s.equals("/")){
                    res = num1/num2;
                }else{
                    throw  new RuntimeException("符号错误");
                }
                //将计算结果再压入栈中
                stack.push(""+res);
            }
        }
        //循环结束后 栈中只剩一个值 那么就是最后的运算结果
        return Integer.parseInt(stack.pop());
    }
}

//定义一个比较运算符优先级的类
class Operator{
    private  static  int ADD = 1;
    private  static  int SUB = 1;
    private  static  int MUL = 2;
    private  static  int DIV = 2;

    public  static int getValue(String item){
        int result = 0;
        switch (item){
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
                default:
                    System.out.println("没有该运算符!");
        }
        return  result;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值