后缀表达式(逆波兰表达式)的求解 及 前缀表达式、中缀表达式、后缀表达式介绍- 栈-Java数据结构和算法

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


前言

生活中最常见的就是中缀表达式了吧,比如(3 + 4) * 5 - 6这种形式,但是对于计算机来说,前缀和后缀表达式更适合运算。详细介绍如下:


一、前缀、中缀、后缀表达式介绍

1. 前缀表达式:
	前缀表达式的运算符位于操作数之前,又称波兰式;
前缀表达式计算机求值:
	从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算
	并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果;
	
2. 中缀表达式:
	常见的运算表达式
中缀表达式的求值:
	中缀表达式是我们人最熟悉的,但是对计算机来说却不好操作(上篇文章中的求解);
	因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式.)
3. 后缀表达式:
	后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
后缀表达式的计算机求值
	从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算
	并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
正常的表达式(中缀)逆波兰表达式 (后缀)
a+ba b +
a+(b-c)a b c - +
a+(b-c)*da b c - d * +
a+d*(b-c)a d b c - * +
a=1+3a 1 3 + =

二、后缀表达式求解

1.实现目标

1. 输入一个逆波兰表达式(后缀表达式),使用栈(Stack), 计算其结果
2. 支持小括号和多位数整数的计算

2.思路分析

1. 先将表达式转换成列表类型(ArrayList)

2.1 将数据和运算符放入ArrayList

代码如下:

public static List<String> getListString(String string) {
    //将字符串分割
    String[] s = string.split(" ");
    List<String> list = new ArrayList<String>();
    for (String a : s) {
        list.add(a);
    }
    return list;
}

2.2 完成后缀表达式的计算

这里用正则表的是匹配多位数字(方便)
//完成对逆波兰表达式的计算
public static int calculate(List<String> list) {
    //创建栈,只需要一个栈即可(数字)
    Stack<String> numStack = new Stack<String>();
    for (String s : list) {
        //正则表达式来取数字方便一些
        if (s.matches("\\d+")) {//匹配的是多位数
            numStack.push(s);
        } else {
            int num1 = Integer.parseInt(numStack.pop());
            int num2 = Integer.parseInt(numStack.pop());
            int ans = 0;
            if (s.equals("+")) {
                ans = num2 + num1;
            } else if (s.equals("-")) {
                ans = num2 - num1;
            } else if (s.equals("*")) {
                ans = num2 * num1;
            } else if (s.equals("/")) {
                ans = num2 / num1;
            }else{
                throw new RuntimeException("运算符错误");
            }
            numStack.push("" + ans);
        }
    }
    return Integer.parseInt(numStack.pop());
}

三、中缀表达式转后缀表达式

用户一般输入的都是中缀表达式,如果能自动转为后缀表达式即可调用方法计算:

1. 思路分析

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

2. 代码实现:

2.1 将中缀表达式转为对应的List

为了运算方便,我们将表达式转为对应的ArrayList

代码如下:

//将中缀表达式转成对应的List
public static List<String> toInfixExperssionList(String expression){
    List<String> list = new ArrayList<>();
    int i = 0;
    String s = "";
    char c; //遍历的
    for(i = 0 ; i < expression.length() ; ){
        //如果c不是数字:----加入到list中即可:
        c = expression.charAt(i);
        if(c > '9' || c < '0'){
            list.add("" + c);
            i++;
        }else{//如果是数字的话, 先清空上次的数据,再拼接
            s = "";
            while(i < expression.length() && (c=expression.charAt(i)) >= '0'
                &&(c=expression.charAt(i)) <= '9'){
                s += c;
                i++;
            }
            list.add(s);
        }
    }
    return list;
}

2.2 中缀转后缀的代码实现:

//将中缀表达式转后缀表达式的代码:
public static List<String> parseSuffixExpresionList(List<String> mid){

    Stack<String> oper = new Stack<String>();
// Stack<String> temp = new Stack<String>();//其实没有必要用,用一个StringBuilder更好
//因为temp在转换的时候, 没有出栈的操作, 所以我们直接用ArrayList<>即可;
    List<String> after = new ArrayList<String>();

    for(String list : mid){
        //如果是一个数字,就进after:
        if(list.matches("\\d+")){
            after.add(list);
        }else if(list.equals("(")){
            oper.push(list);
        }else if(list.equals(")")){//右括号的话弹栈
            while(!oper.peek().equals("(")){
                after.add(oper.pop());
            }
            //两个括号抵消
            oper.pop();
        }else{
            //若读取的list操作符优先级 小于等于  oper栈顶部的优先级,,oper出栈到 after
            while(!oper.isEmpty() &&
                    Operation.getValue(list) <= Operation.getValue(oper.peek())){
                after.add(oper.pop());
            }
            //直到list操作符优先级 大于oper栈顶优先级:
            oper.push(list);
        }
    }
    while (!oper.isEmpty()) {
        after.add(oper.pop());
    }
    return after;
}

总结

只要按照步骤及进行即可,主要还是对于多位数的处理,以及对于括号的处理;

完整代码:

package;

import javax.imageio.event.IIOReadProgressListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.SubmissionPublisher;
import java.util.stream.StreamSupport;

/**
 * @author Weilei Zhang
 * @create 2021-03-01 19:47
 */
public class PolanNotation {
    public static void main(String[] args) {

        //将中缀表达式转后缀表达式的代码:
        //但是因为扫描字符串不方便,所以在此将中缀表达式转成对应的List
        String expresion = "1+((2+3)*4)-5";
//进行转换为List<String>
        List infixList = toInfixExperssionList(expresion);
        System.out.println("前缀表达式List = " + infixList);

        List<String> suffix = parseSuffixExpresionList(infixList);
        System.out.println("后缀表达式List: " + suffix);

        System.out.println(expresion + " = " + calculate(suffix));


//        //先写一个逆波兰表达式(后缀表达式)
//        String str = "3 4 + 5 * 6 -";//(3 + 4) * 5 - 6 = 29
//        String str2 = "12 3 + 4 5 * +";//(12 + 3) + 4 * 5 = 35
//        //一个表达式 依次将数据和运算符放到ArrayList中, 配合栈,完成计算
//        List<String> list = getListString(str2);
//        System.out.println("list = " + list);
//        System.out.println("answer = " + calculate(list));
    }


    public static List<String> getListString(String string) {
        //将字符串分割
        String[] s = string.split(" ");
        List<String> list = new ArrayList<String>();
        for (String a : s) {
            list.add(a);
        }
        return list;
    }

    //    完成对逆波兰表达式的计算
    public static int calculate(List<String> list) {
        //创建栈,只需要一个栈即可(数字)
        Stack<String> numStack = new Stack<String>();
        for (String s : list) {
            //正则表达式来取数字方便一些
            if (s.matches("\\d+")) {//匹配的是多位数
                numStack.push(s);
            } else {
                int num1 = Integer.parseInt(numStack.pop());
                int num2 = Integer.parseInt(numStack.pop());
                int ans = 0;
                if (s.equals("+")) {
                    ans = num2 + num1;
                } else if (s.equals("-")) {
                    ans = num2 - num1;
                } else if (s.equals("*")) {
                    ans = num2 * num1;
                } else if (s.equals("/")) {
                    ans = num2 / num1;
                }else{
                    throw new RuntimeException("运算符错误");
                }
                numStack.push("" + ans);
            }
        }
        return Integer.parseInt(numStack.pop());
    }


    //将中缀表达式转后缀表达式的代码:
    public static List<String> parseSuffixExpresionList(List<String> mid){

        Stack<String> oper = new Stack<String>();
//        Stack<String> temp = new Stack<String>();//其实没有必要用,用一个StringBuilder更好
        //因为temp在转换的时候, 没有出栈的操作, 所以我们直接用ArrayList<>即可;
        List<String> after = new ArrayList<String>();

        for(String list : mid){
            //如果是一个数字,就进after:
            if(list.matches("\\d+")){
                after.add(list);
            }else if(list.equals("(")){
                oper.push(list);
            }else if(list.equals(")")){//右括号的话弹栈
                while(!oper.peek().equals("(")){
                    after.add(oper.pop());
                }
                //两个括号抵消
                oper.pop();
            }else{
                //若读取的list操作符优先级 小于等于  oper栈顶部的优先级,,oper出栈到 after
                while(!oper.isEmpty() &&
                        Operation.getValue(list) <= Operation.getValue(oper.peek())){
                    after.add(oper.pop());
                }
                //直到list操作符优先级 大于oper栈顶优先级:
                oper.push(list);
            }
        }
        while (!oper.isEmpty()) {
            after.add(oper.pop());
        }
        return after;
    }

    //将中缀表达式转成对应的List
    public static List<String> toInfixExperssionList(String expression){
        List<String> list = new ArrayList<>();
        int i = 0;
        String s = "";
        char c; //遍历的
        for(i = 0 ; i < expression.length() ; ){
            //如果c不是数字:----加入到list中即可:
            c = expression.charAt(i);
            if(c > '9' || c < '0'){
                list.add("" + c);
                i++;
            }else{//如果是数字的话, 先清空上次的数据,再拼接
                s = "";
                while(i < expression.length() && (c=expression.charAt(i)) >= '0'
                    &&(c=expression.charAt(i)) <= '9'){
                    s += c;
                    i++;
                }
                list.add(s);
            }
        }
        return list;
    }

}

class Operation{
    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 2;
    private static int DIV = 3;

    //获得优先级
    public static int getValue(String s){
        int result = 0;
        switch (s){
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                result = -1;
                break;
        }
        return result;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值