java--->栈的中缀(波兰表达式),前缀,后缀(逆波兰表达式)

文章目录

      • 中缀表达式
      • 前缀表达式
      • 后缀表达式
      • 如何把中缀表达式转换为后缀表达式

代码配套食用口味更佳~~~~
栈,先入后出

先来个栈的手写代码,这里面你要知道有哪些方法,比如jdk自带的Stack的栈也就那几种方法。

package a_heima.stack;

/**
 * @ClassName MyStack
 * @Description TODO 使用数组表示栈
 * @Author zyhh
 * @date 2023/12/27 16:50
 * @version: 1.0
 */

import java.lang.reflect.Array;

/**
 * 特征是:先入后出,变化的一段是栈顶,固定的一端是栈底
 */
public class MyStack {
    public static void main(String[] args) {
//        栈测试
        ArrayStack arrayStack = new ArrayStack(4);
        System.out.println(arrayStack.isEmpty());
        System.out.println(arrayStack.isFull());
        arrayStack.push(1);
        arrayStack.push(2);
        arrayStack.push(3);
        arrayStack.push(4);
        System.out.println("是否栈满::");
        System.out.println(arrayStack.isFull());
        arrayStack.push(5);
//        arrayStack.push(5);
        arrayStack.list();
        System.out.println("==========分割线==================");
        System.out.println("弹出的数据为:"+arrayStack.pop());
        arrayStack.list();
    }
}
class ArrayStack{
    private int maxSize;
    private int[] stack;
    private int top=-1;

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack=new int[maxSize];
    }
//    栈满
    public boolean isFull(){
        return (stack.length<=top+1);
    }
//    栈空
    public boolean isEmpty(){
        return top==-1;
    }
//    入栈
    public void push(int value){
//        先判断栈是否满
        boolean full = isFull();
        if(full){
            System.out.println("满了,别加数据了!!!");
            return;
        }
        top++;
        stack[top]=value;

    }
//    出栈,就是将栈顶的数据返回
    public int pop(){
        if(isEmpty()){
            throw  new RuntimeException("栈空,无数据");
        }
        int value=stack[top];
        top--;
        return value;
    }
//    遍历,从栈顶开始显示
    public void list(){
        if(isEmpty()){
            System.out.println("栈空");
            return;
        }
        for(int i=top;i>=0;i--){
            System.out.println(stack[i]);
        }
    }
}

中缀表达式

用中缀表达式表示计算机计算步骤,啥是中缀表达式,这么说吧,咱们学数学的时候的思考步骤和中缀表达式很像。就像先乘除后加减一样。
中缀表达式对于我们人来说好理解,对于计算机来说不好操作。因此,在计算结果时,往往会将中缀表达式转为其他表达式操作(比如转成后缀表达式)。

里面的注解写的比较清楚,常回来看看

package a_heima.stack;

/**
 * @ClassName Computer
 * @Description TODO 计算器,使用栈表示计算器的结果,两个栈,数栈,符号栈
 * @Description TODO 注意这个是中缀表达式
 * @Author zyhh
 * @date 2023/12/28 16:56
 * @version: 1.0
 */

//    首先,通过index来遍历表达式。
//    如果发现index是数,就直接入数栈,如果扫描到的是符号,就分如下情况。
//    1.如果当前符号栈为空,就直接入栈。
//    2。如果符号栈部位空,有操作符,就进行比较,如果当前的操作符的优先级小于或等于栈中的操作符,先不要把当前的入栈,需要从数栈中pop出两个数,在从符号栈这种pop出一个符。
//    将得到的结果入数栈,然后将当前的操作符入符号栈。
//    如果当前符号优先级大于栈中的操作符,就直接1入栈。
//    当表达式扫描完毕后,就顺序的从数栈和符号栈中pop出相应的数据和符号。
//    减法运算时,是下面的数据减去上面的数据。

/**
 * 计算器代码实现
 */
public class Computer {
    public static void main(String[] args) {
        String exp = "70+2*6-4";
//        创建两个栈,数栈,一个符号栈
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(10);
//        定义需要的相关变量
        int index=0;
        int num1=0;
        int num2=0;
        int oper=0;//存放操作符
        int res=0;//存放结果
        char ch=' ';
        String keepNum="";//存放拼接的多位数字
        while(true){
//            一次得到字符
            ch=exp.substring(index,index+1).charAt(0);
//            如果ch为符号
            if(operStack.isOper(ch)){
//                再判断当前符号栈是否为空,为空则直接入栈
                if(operStack.isEmpty()){
                    operStack.push(ch);
                }else {//不为空就判断优先级大小
                    if(operStack.pro(ch)<=operStack.pro(operStack.peek())){
                        num1=numStack.pop();
                        num2=numStack.pop();
                        oper=operStack.pop();
                        res = operStack.cal(num1, num2, oper);
//                       把运算结果放入数栈
                        numStack.push(res);
//                        然后把当前的操作符放进去
                        operStack.push(ch);
                    }else {
                        operStack.push(ch);//直接放
                    }
                }
            }else {//如果ch是数
//                numStack.push(ch-48);
//                当处理多位数时,不能吧数立即入栈,也可能为多位数,向exp的表达式index再向后移动,是数就扫描,是符号再入栈。
                keepNum+=ch;
//                如果ch是exp最后一位了。直接放入栈。
                if(index==exp.length()-1){
                    numStack.push(Integer.valueOf(keepNum));
                    keepNum="";
                }else {
                    if(operStack.isOper(exp.substring(index+1,index+2).charAt(0))){
                        numStack.push(Integer.valueOf(keepNum));
                        //重要!!!!!!!!
                        keepNum="";
                    }else {

                    }
                }
            }
//            index+1
            index++;
            if(index>=exp.length()){
                break;
            }
        }
//     上面扫描完毕了,符号栈和数栈该进去的也都进去了,就该挨个出来计算了
        while (true){
            if(operStack.isEmpty()){
                break;
            }
            num1=numStack.pop();
            num2=numStack.pop();
            oper=operStack.pop();
            int cal = numStack.cal(num1, num2, oper);
            numStack.push(cal);
        }
//  最后数栈最后值就是结果
        res=numStack.pop();
        System.out.println("最后计算的结果是:::"+res);
    }
}
//先创建一个栈
class ArrayStack2{
    private int maxSize;
    private int[] stack;
    private int top=-1;

    public ArrayStack2(int maxSize) {
        this.maxSize = maxSize;
        stack=new int[maxSize];
    }
    //    栈满
    public boolean isFull(){
        return (stack.length<=top+1);
    }
    //    栈空
    public boolean isEmpty(){
        return top==-1;
    }
    //    入栈
    public void push(int value){
//        先判断栈是否满
        boolean full = isFull();
        if(full){
            System.out.println("满了,别加数据了!!!");
            return;
        }
        top++;
        stack[top]=value;

    }
    //    出栈,就是将栈顶的数据返回
    public int pop(){
        if(isEmpty()){
            throw  new RuntimeException("栈空,无数据");
        }
        int value=stack[top];
        top--;
        return value;
    }
    //    遍历,从栈顶开始显示
    public void list(){
        if(isEmpty()){
            System.out.println("栈空");
            return;
        }
        for(int i=top;i>=0;i--){
            System.out.println(stack[i]);
        }
    }
//    返回数据的优先级
    public int pro(int oper){
        if(oper=='*'||oper=='/'){
            return 1;
        }else if (oper=='+'||oper=='-'){
            return 0;
        }else {
            return -1;//嘉定目前的表达式只有+-*/
        }
    }
//    把符号栈上一个符号优先级获取到
    public int peek(){
        return stack[top];
    }
//    判断是不是一个运算符
    public boolean isOper(char val){
        return val=='+'|| val == '-'||val=='*'||val=='/';
    }
//    计算方法
    public int cal(int num1,int num2,int oper){
        int res=0;//用于存放计算的结果
        switch (oper){
            case '+':
                res=num1+num2;
                break;
            case '-':
                res=num2-num1;//注意这里的顺序
                break;
            case '*':
                res=num1*num2;
                break;
            case '/':
                res=num2/num1;//注意这里顺序
                break;

        }
        return res;
    }
}

前缀表达式

前缀表达式:(又名波兰表达式)
右至左扫描表达式表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的运算,并将结果入栈:重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果。
例如:(3+4)5-6对应的前缀表达式:- * + 3 4 5 6,针对前缀表达式求值步骤如下:
1):从右至左扫描,将6,5,4,3,入栈
2):遇到+运算符,因此弹出3和4(3为栈顶,4为次顶),计算出3+4的值,得7,再将7入栈。
3):接下来是
运算,因此弹出7和5,计算出7*5=35,将35入栈。
4):最后是运算符,计算出35-6的值,即29,由此的出最终结果

后缀表达式

后缀表达式:(又称逆波兰表达式)
听名字就知道和前缀表达式有一部分相似,就是逆了而已,看看是怎么逆的:
后缀表达式就是把前缀表达式运算符位于操作数之后了。
比如:
正常表达式 :逆波兰表达式
a+b :ab+
a+(b-c) :abc-+
a+(b-c)d :abc-d*+
a+d
(b-c) :adbc-*+
a=1+3 :a13=

对于上面的逆波兰表达式:你可以了解到,先出现的那个符号必定是优先级最高的。就像那个a+(b-c)是abc-+中-符号在()里,所以优先级最高,就最先出现。

为了更好的理解,现在用后缀表达式描述求值过程。
从左到右一次扫描,遇到数字时,将数字压入栈,遇到运算符时,弹出栈顶的俩数,用运算符对他们做相应的计算,并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值就是表达式结果。
举例:(3+4)5-6对应后缀:34+56-,其计算步骤为:
1):从左到右扫描,将3和4压入栈
2):遇到+运算符,因此弹出4和3(4为栈顶,3为次顶),计算3+4,得7入栈
3):将5入栈;
4):接下来就是运算,因此弹出5和7,计算出75=35,将35入栈;
5):将6入栈
6):最后是-运算符,计算出35-6的值,即29,这就是最后结果

逆波兰计算器代码:

package a_heima.stack;

import org.junit.jupiter.api.Test;

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

/**
 * @ClassName NiBoLan
 * @Description TODO 逆波兰计算器来计算
 * @Description TODO
 * @Author zyhh
 * @version: 1.0
 */
public class NiBoLan {
    public static void main(String[] args) {
//        将算式(3+4)*5-6直接转化为逆波兰表达式。
        String exp="3 4 + 5 * 6 -";
//        
        Stack stack=new Stack();
//        挨个扫描太慢,直接放到List当中
//        思路:
//        1.先放到list。
//        2.有ArrayList传递给一个方法,配合栈完成计算
        List<String> list=getListString(exp);
        int cal = cal(list);
        System.out.println("计算的数值为:"+cal);

    }
//    将字符串转化为集合
    public static List getListString(String str){
        ArrayList list = new ArrayList();
        String[] s = str.split(" ");
        for (String s1 : s) {
            list.add(s1);
        }
        return list;
    }
//    计算
    public static int cal(List<String> ls){
//        创建给栈,只需要一个栈
        Stack stack = new Stack();
        for (String l : ls) {//        遍历list
            if(l.matches("\\d+")){//如果匹配的是多位数
                stack.push(l);
            }else {//否则就是符号,此时就得数据出栈
                int num1 = Integer.parseInt(String.valueOf(stack.pop()));
                int num2 = Integer.parseInt(String.valueOf(stack.pop()));
                int res=0;
                if(l.equals("+")){
                    res= num2+num1;
                }else if(l.equals("-")){
                    res= num2-num1;
                }else if(l.equals("*")){
                    res= num2*num1;
                }else if(l.equals("/")){
                    res= num2/num1;
                }else {
                    throw new RuntimeException("运算有误");
                }
//                把res入栈
                stack.push(res);
            }
        }
        return Integer.parseInt(String.valueOf(stack.pop()));
    }
}

如何把中缀表达式转换为后缀表达式

前面我们已经讲到,中缀表达式适合我们人来的计算习惯,但后缀表达式才是适合计算机的计算。那我们如何把中缀转前缀呢?这样的话,我们平时写出来的表达式就可以自动转为后缀来让计算机加快计算。
下面一起来look!

先分析一下步骤:举例:中缀表达式:1+((2+3)*4)-5

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

中缀·表达式转后缀表达式代码实现:

package a_heima.stack;

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

/**
 * @ClassName Tran
 * @Description TODO 中缀表达式转后缀表达式代码实现
 * @Author zyhh
 * @date 2024/1/2 11:19
 * @version: 1.0
 */
public class Tran {
    public static void main(String[] args) {
//        1+((2+3)*4)-5==》1 2 3 + 4 * + 5 -
        String exp="1+((2+3)*4)-5";
        List list = forList(exp);//[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
        System.out.println(list);
        List tran = tran(list);
        System.out.println("后缀表达式为:::"+tran);
    }

    /**
     * TODO    把字符串加到集合中。这里也考虑到了多位数字的情况
     * @param str
     * @return
     */
    public static List forList(String str){
        List list = new ArrayList();

        int i=0;
        String str2 = "";//多位数拼接
        char cc;//每遍历到一个字符,就放入c
        do{
            if((cc=str.charAt(i))<48||(cc=str.charAt(i))>57){//如果cc是非数字就加入到list。
                list.add(""+cc);
                i++;
            }else {
                str2="";
                while((i< str.length()) && (cc=str.charAt(i))>=48 && (cc=str.charAt(i))<=57){
                    str2 += cc;
                    i++;
                }
                list.add(str2);
            }

        }while(i<str.length());
        return list;
    }

    /**
     * 中缀转后缀
     * @param ls
     */
    public static List<String> tran(List<String> ls) {
//        定义一个栈一个list集合。由于s2可以明显看出在整个在转换过程中没有pop操作,最后又需要逆序输出,list功能已经够了。
        Stack<String> s1 = new Stack();//符号栈
        List<String> s2 = new ArrayList();//存储中间结果
//        遍历ls
        for (String item : ls) {
            if (item.matches("\\d+")) {//如果这个元素是个数,就进集合
                s2.add(item);
            } else if (item.equals("(")) {//如果是左括号,则进符号栈
                s1.push(item);
            } else if (item.equals(")")) {//如果是右括号,就一次弹出符号进s2
                while (!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();//将左括号(弹出s1,来消除小括号
            } else {
//                当item优先级小于或等于栈顶运算符,就把栈顶的弹出加入s2
                while (s1.size() != 0 && com(item) <= com(s1.peek())) {
                    s2.add(s1.pop());
                }
                s1.push(item);//还要把优先级小的放进去
            }
        }
        //        将剩余的加入栈中
        while (s1.size() != 0) {
            s2.add(s1.pop());
        }
        return s2;
    }
//    比较优先级高低的方法
    public static int com(String a1){
        int a=0;
        if(a1.equals("*")||a1.equals("/")){
            a= 2;
        }else if(a1.equals("+")||a1.equals("-")){
            a= 1;
        }else {
            System.out.println("运算符异常,请检查");
        }
        return a;
    }
}

============================== end========================================

你可能感兴趣的:(数据结构,java,windows,开发语言)