学习笔记-前缀、中缀和后缀表达式运算

前缀表达式

前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。前缀表达式也称为“波兰表达式”。例如,- 1 + 2 3,它等价于1-(2+3)。

求值方法

从右至左扫描表达式,若当前字符是数字则一直到数字串的末尾再记录下来,若为运算符,则将右边离得最近的两个“数字串”作相应运算,然后以此作为一个新的“数字串”并记录下来;扫描到表达式最左端时扫描结束,最后运算的值即为表达式的值。

例如:对前缀表达式“- 1 + 2 3”求值,扫描到3时,记录下这个数字串,扫描到2时,记录下这个数字串,当扫描到+时,将+右移做相邻两数字串的运算符,记为2+3,结果为5,记录下5这个新数字串,然后继续向左扫描,扫描到1时,记录下这个数字串,扫描到-时,将-右移做相邻两数字串的运算符,记为1-5,结果为-4,此时关于这个表达式的全部运算已完成,故表达式的值为-4。

中缀表达式

运算符在表达式中间,就是日常的算式形式。比如1+2*3

求值方法

此时,中缀表达式写为一个字符串,返回最后的结果数字。
思路:
创建两个栈,一个用来存放数字,一个用来存放运算符。
通过一个index指针遍历字符串。
如果该符号是数字,考虑多位数的情况,如果是最后一位,直接加入数栈,如果不是最后一位且下一位是数字,进行字符串的拼接,如果不是最后一位且下一位是运算符,将字符串加入数栈。
如果该符号是运算符。如果运算符栈为空,直接加入运算符栈,如果不为空,当该符号的优先级大于运算符栈顶的符号时,直接加入运算符栈,当该符号的优先级小于等于运算符栈顶的符号时,从数栈pop出两个数,从运算符栈pop出一个运算符,计算后,结果加入数栈,该符号加入运算符栈。
遍历结束后,依次从数栈和运算符栈中pop元素运算,直到数栈只剩下一个数,或运算符栈空。此时剩下的数为结果。

代码实现

package DataStructure.Stack;

/**中缀
 * 综合计算器
 */
public class calculator {
    public static void main(String[] args) {
        String str="0-5-5-5";
        //数字栈
        ArrayStack2 numStack=new ArrayStack2(10);
        //字符栈
        ArrayStack2 operStack=new ArrayStack2(10);
        //用一个index挨个扫描str
        int index=0;
        int num1=0;
        int num2=0;
        int oper=0;
        int res=0;
        char ch=' ';
        String s="";

        while (true){
            ch=str.substring(index,index+1).charAt(0);
            //判断ch是不是运算符,是运算符
            if(operStack.isOper(ch)){
                //栈空,直接加入
                if(operStack.isEmpty()){
                    operStack.push(ch);
                }else {
                    //栈不空,判断优先级,大于栈顶的直接加入
                    if (operStack.youxian(ch)>operStack.youxian(operStack.peek())){
                        operStack.push(ch);
                    }else {
                        //小于等于时,从数字栈弹两个数字,符号栈弹一个符号,运算完结果存数字栈,符号存符号栈
                        num1=numStack.pop();
                        num2=numStack.pop();
                        oper=operStack.pop();
                        res=operStack.cal(num1,num2,oper);
                        numStack.push(res);
                        operStack.push(ch);
                    }
                }
            }else {
                //是数字,扫描下一位,如果是符号就加入栈,否则进行拼接
                s+=ch;
                //是不是最后一位
                if(index==str.length()-1){
                    numStack.push(Integer.parseInt(s));
                    s="";
                }else{
                    if(operStack.isOper(str.substring(index+1,index+2).charAt(0))){
                        numStack.push(Integer.parseInt(s));
                        s="";
                    }
                }
            }
            index++;
            //index=字符串长度,说明扫描完了
            if(index>str.length()-1){
                break;
            }
        }

        //扫描完后,不断出栈,直到符号栈空
        while (true){
            if(operStack.isEmpty()){
                break;
            }else {
                num1=numStack.pop();
                num2=numStack.pop();
                oper=operStack.pop();
                res=operStack.cal(num1,num2,oper);
                numStack.push(res);
            }
        }
        System.out.println(str+"="+numStack.pop());



    }
}
class ArrayStack2{
    //容量
    public int max;
    public int[] stack;
    //栈顶
    public int top=-1;
    public ArrayStack2(int max){
        this.max=max;
        stack=new int[this.max];
    }
    //空
    public boolean isEmpty(){
        return top==-1;
    }
    //满
    public boolean isFull(){
        return top==max-1;
    }
    //入栈
    public void push(int num){
        if(isFull()){
            System.out.println("栈满");
            return;
        }else {
            top++;
            stack[top]=num;
        }
    }
    //出栈
    public int pop(){
        if(isEmpty()){
            throw new RuntimeException("空栈");
        }else {
            int value=stack[top];
            top--;
            return value;
        }
    }
    //展示
    public void show(){
        if(isEmpty()){
            System.out.println("栈空");
        }else {
            for (int i = top; i >=0; i--) {
                System.out.printf("stack[%d]:%d\n",i,stack[i]);
            }
        }
    }
    //返回栈顶
    public int peek(){
        return stack[top];
    }
    //判断运算符的优先级,只支持加减乘除
    public int youxian(int oper){
        if(oper=='*' || oper=='/'){
            return 1;
        }else if(oper=='+' ||oper=='-'){
            return 0;
        }else {
            return -1;
        }
    }
    //运算
    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;
            default:
                break;
        }
        return res;
    }
    //判断是不是运算符
    public boolean isOper(char ch){
        return ch=='*'||ch=='/'||ch=='+'||ch=='-';
    }
}

后缀表达式

将运算符写在操作数之后的一种表达式。也称为“逆波兰表达式”。对电脑来说后缀表达式更好运算,因此一般是把中缀表达式转化成后缀,然后计算。

中缀表达式转后缀表达式

思路:
首先将中缀表达式字符串转为list方便之后的运算。依次扫描字符串,如果是运算符,直接加入list,如果是数字,考虑多位数,当下一位是数字时,进行字符串的拼接,当下一位是运算符时,把字符串加入list。

//字符串转化为list
    public static List<String> getList(String exp){
        int i=0;
        String str;
        List<String> list=new ArrayList<String>();
        do {
            char item=exp.charAt(i);
            if(item<48 || item>57){
                list.add(item+"");
                i++;
            }else {
                str="";
                //如果是数,考虑多位数
                while(i<exp.length() && exp.charAt(i)>=48 && exp.charAt(i)<=57){
                    str+=exp.charAt(i);
                    i++;
                }
                list.add(str);
            }
        }while (i<exp.length());
        return list;

    }

遍历中缀表达式list。创建两个栈s1,s2。s2也可以用数组代替,因为s2只有加入的操作。
当是数字时,直接加入s2。
当是运算符时,如果s1是空的或者s1栈顶是左括号(,直接把该符号加入s1;否则,判断优先级,如果该符号的优先级大于s1栈顶符号的优先级,把该符号加入s1,如果该符号的优先级小于等于s1栈顶符号的优先级,把s1栈顶运算符弹出加入s2,并重新判断优先级。
当是左括号(时,直接加入s1.
当是右括号)时,把s1栈顶运算符弹出加入s2,直到遇到左括号(,之后把左括号弹出。
遍历结束后,将s1元素依次弹出加入s2.
最后s2倒序即为后缀表达式。

代码实现

//中缀表达式转为后缀
    public static List<String> toHou(List<String> list){
        //创建一个符号栈,一个结果栈(方便输出改为数组)
        Stack<String> s1=new Stack<String>();
        List<String> s2=new ArrayList<>();
        //遍历前缀表达式
        for (String item:list) {
            //是数字,直接加入s2
            if(item.matches("\\d+")){
                s2.add(item);
            }else if(item.equals("(")){//是(直接加入s1
                s1.push(item);
            }else if(item.equals(")")){
                //如果是右括号“)”,则依次弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这一对括号丢弃
                while (!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();
            }else{//是运算符
                //当 item 的优先级小于等于 s1 栈顶运算符, 将 s1 栈顶的运算符弹出并加入到 s2 中,再次转到(4.1)与 s1 中新的栈顶运算符相比较
                while (!s1.empty() && !s1.peek().equals("(")&& youxian(s1.peek())>=youxian(item)){
                        s2.add(s1.pop());
                }
                s1.push(item);
            }
        }
        //将 s1 中剩余的运算符依次弹出并加入 s2
        while (!s1.empty()) {
            s2.add(s1.pop());
        }
        return s2;
    }
    //判断运算符优先级
    public static int youxian(String s){
        int res=0;
        switch (s){
            case "+":
                res= 1;
                break;
            case "-":
                res= 1;
                break;
            case "*":
                res= 2;
                break;
            case "/":
                res= 2;
                break;
            default:
                break;
        }
        return res;
    }

后缀表达式的运算

创建一个栈。遍历后缀表达式。
如果是数字,直接压入栈。
如果是运算符,从栈中pop出两个元素,进行运算(顺序为后一个对前一个),将结果压入栈中。
遍历结束后,栈中剩下的一个数字就是结果。

代码实现

//运算后缀表达式
    public static int cal(List<String> exp){
        Stack<String> stack=new Stack<>();
        int num1;
        int num2;
        for (String s:exp) {
            if(s.matches("\\d+")){
                stack.push(s);
            }else {
                num1=Integer.parseInt(stack.pop());
                num2=Integer.parseInt(stack.pop());
                int res=0;
                if(s.equals("+")){
                    res=num1+num2;
                }else if(s.equals("-")){
                    res=num2-num1;
                }else if(s.equals("*")){
                    res=num2*num1;
                }else if(s.equals("/")){
                    res=num2/num1;
                }else {
                    throw new RuntimeException("操作符错误");
                }
                stack.push(res+"");
            }

        }
        return Integer.parseInt(stack.pop());
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值