Java与数据结构——栈(前缀、中缀、后缀表达式)

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

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

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

1)从右至左扫描,将6、5、4、3压入堆栈;

2)遇到 “+” 运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算3+4的值7,将7入栈;

3)遇到 “X” 运算符,因此弹出7和5,计算7 X 5的值35,将35入栈;

4)遇到 “-” 运算符,因此弹出35和6,计算35-6的值29,由此得到最终结果29。

中缀表达式

中缀表达式是最常见的运算表达式,如:3+4)X 5-6,中缀表达式的求值是人们最熟悉的,但是对计算机来说却不好操作(需要判断运算符的优先级)。因此,在计算结果时,往往会将中缀表达式转成其他表达式来操作。

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

后缀表达式与前缀表达式相似,只是运算符位于操作数之后,例如(3+4)X 5-6对应的后缀表达式就是3  4  +  5  X  6  -  。后缀表达式的计算过程就是从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素和栈顶元素),并将结果入栈;重复上述过程直到表达式的右端,最后运算得出的值即为表达式的结果。

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

1)从右至左扫描,将3和4压入堆栈;

2)遇到 “+” 运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算3+4的值7,将7入栈;

3)遇到 5 ,将 5 入栈;

4)遇到 “X” 运算符,因此弹出5和7,计算出 7 X 5 = 35,将35入栈;

5)遇到 6 ,将 6 入栈;

6)遇到 “-” 运算符,计算出 35 - 6 = 29,29即为最终的计算结果。

代码实现:

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

public class PolandNotation {
    public static void main(String[] args) {
        //先定义逆波兰表达式
        //(3+4)X 5-6 ==>  3 4 + 5 X 6 -
        String suffixExpression = "30 4 + 57 * 6 -";
        //思路
        //1. 先将 "3 4 + 5 * 6" 放到 ArrayList
        //2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈进行计算
        List<String> rpnList = getListString(suffixExpression);
        System.out.println(rpnList);
        System.out.println("计算结果是:" + calculate(rpnList));
    }
    //将一个逆波兰表达式,依次将数据和运算符放到 ArrayList中
    public static List<String> getListString(String suffixExpression){
        //将suffixExpression分割
        String[] split = suffixExpression.split(" ");
        List<String> list = new ArrayList<>();
        for (String ele:split){
            list.add(ele);
        }
        return list;
    }
    public static int calculate(List<String> ls){
        //创建一个栈
        Stack<String> stack = new Stack<>();
        //遍历ls
        for (String item:ls){
            //使用正则表达式取数
            if (item.matches("\\d+")){//利用正则表达式匹配多位数
                //入栈
                stack.push(item);
            }else {
                //pop出两个数,运算,再入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")){
                    res = num1 + num2;
                }else if (item.equals("-")){
                    res = num1 - num2;
                }else if (item.equals("*")){
                    res = num1 * num2;
                }else if (item.equals("/")){
                    res = num1 / num2;
                }else {
                    throw new RuntimeException("运算符异常!");
                }
                //将 res 入栈
                stack.push(res + "");
            }
        }
        //最终留在stack中的数据就是运算结果
        return Integer.parseInt(stack.pop());
    }
}

中缀表达式转后缀表达式

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

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

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

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

        4.1 如果 s1 为空,或栈顶运算符为左括号 “( ” ,则直接将此运算符入栈;

        4.2 否则,若此运算符的优先级比栈顶运算符高,则将此运算符压入 s1;

                        4.3否则,将 s1 栈顶的运算符弹出并压入到 s2 中,然后转到 4.2,与新的栈顶                                     运算符进行比较 ;

5) 若遇到括号时:

        5.1 如果是左括号 “( ”,则直接压入 s1;

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

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

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

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

 

package stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class infixToSuffix {
    public static void main(String[] args) {
        //完成将一个中缀表达式转成后缀表达式的功能
        //说明
        //1. 1+((2+3)*4)-5 ==> 1 2 3 + 4 * 5 -
        //2. 因为直接对 String 进行操作不方便,因此先将 “1+((2+3)*4)-5” ==> 中缀表达式对应的List
        //即“1+((2+3)*4)-5” ==> ArrayList [1,+,(,(,2,+,3),*,-,5]
        //3. 将得到的中缀表达式List ==> 后缀表达式List
        //即 ArrayList [1,+,(,(,2,+,3),*,-,5] ==> ArrayList[1,2,3,+,4,*,+,5,-]
        String expression = "1+((2+3)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("对应的中缀表达式List:"+infixExpressionList);
        System.out.println();
        List<String> parseSuffixExpressionList = parseSuffixExpressionList(infixExpressionList);
        System.out.println("对应的后缀表达式List:"+parseSuffixExpressionList);
    }
    //将中缀表达式转换成对应的List
    public static List<String > toInfixExpressionList(String s){
        //定义一个List,存放中缀表达式对应的内容
        List<String > ls = new ArrayList<>();
        int index = 0;//定义一个索引,用于遍历中缀表达式字符串
        String str;//用于做多位数的拼接
        char ch;//每遍历一个字符,就放入ch
        do{
            //如果ch是一个非数字,加入到ls
            if ((ch = s.charAt(index)) < 48 || (ch = s.charAt(index)) < 57){
                ls.add(""+ch);
                index++;
            }else {
                //如果ch是一个数,需要考虑多位数
                str = "";//现将str置为空串  '0'[48] -> '9'[57]
                while (index < s.length() && (ch = s.charAt(index)) >= 48 && (ch = s.charAt(index)) <= 57){
                    str += ch;
                    index++;
                }
                ls.add(str);
            }
        }while (index < s.length());
        return ls;
    }
    //将得到的中缀表达式List ==> 后缀表达式List
    public static List<String> parseSuffixExpressionList(List<String> ls){
        //定义两个栈
        Stack<String> s1 = new Stack<>();
        //说明:s2在整个操作中没有pop操作,后面还需逆序输出
        //因此用栈比较麻烦,就不采用Stack<String> 直接用List<String> s2
        //Stack<String> s2 = new Stack<>();
        List<String> s2 = new ArrayList<>();//储存中间结果的List2

        //遍历ls
        for (String item:ls){
            //如果是一个数,加入s2
            if (item.matches("\\d+")){
                s2.add(item);
            }else if (item.equals("(")){
                s1.push(item);
            }else if (item.equals(")")){
                //如果是右括号 “ )”,则依次弹出 s1栈顶的运算符,并压入 s2 ,直到遇到左括号为止,此时,将这一对括号丢弃;
                while (!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();//将“)”弹出,消除小括号
            }else{
                //当item优先级小于等于栈顶运算符优先级,需编写一个类返回运算符对应优先级
                //将 s1 栈顶的运算符弹出并压入到 s2中,然后转到 4.2,与s1新的栈顶运算符进行比较
                while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                //还需要讲item压入栈s1
                s1.push(item);
            }
        }
        while (!s1.isEmpty()){
            s2.add(s1.pop());
        }
        return s2;//因为是存放到List,因此按顺序输出是对应的后缀表达式
    }
}
//编写一个类Operation 返回运算符对应的优先级
class Operation{
    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 operation){
        int res =0;
        switch (operation){
            case "+":
                res = ADD;
                break;
            case "-":
                res = SUB;
                break;
            case "*":
                res = MUL;
                break;
            case "/":
                res = DIV;
                break;
            default: ;
                break;
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值