栈实现逆波兰计算器

本文介绍了栈的概念及其在子程序调用、递归处理、表达式转换和二叉树遍历等场景的应用。通过Java代码展示了如何用数组模拟栈的入栈、出栈操作,并实现了一个支持加减乘除的计算器。此外,详细阐述了前缀、中缀和后缀表达式的特点及计算方法,并给出了一个逆波兰计算器的实现,包括中缀转后缀表达式和计算后缀表达式的步骤。
摘要由CSDN通过智能技术生成

一、栈

1、概念
英文名stack,是一个先进后出的有序列表。插入和删除只能在栈顶进行操作,栈底固定。入栈push出栈pop
2、应用场景
1)子程序的调用:进入子程序之前,会先将下指令的程序地址存入栈中,子程序执行完成后,将地址取出,返回到原来的程序中。
2)处理递归调用:和子程序调用类似,只是除了程序地址之外,还会将参数、区域变量等存进栈中。
3)表达式的转换[中缀表达式转换后缀表达式]与求值
4)二叉树遍历
5)图形的深度优先搜索法
3、数组模拟栈,入栈,出栈等操作

/**
 * @author chengxn
 * @date 2020/10/11
 * 数组模拟栈
 */
public class ArrayStackDemo {
    public static void main(String[] args) {
        ArrayStack arrayStack = new ArrayStack(4);
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);
        String key = "";

        while(loop){
            System.out.println("show:循坏栈");
            System.out.println("push:入栈");
            System.out.println("pop:出栈");
            System.out.println("exist:退出程序");
            key = scanner.next();
            switch(key){
                case "push":
                    System.out.println("请输入数值:");
                    int value = scanner.nextInt();
                    arrayStack.push(value);
                    break;
                case "pop":
                    try{
                        System.out.println(arrayStack.pop());
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case "show":
                    arrayStack.show();
                    break;
                case "exist":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序结束");
    }

}

class ArrayStack {
    //栈的大小
    private int maxSize;
    private int[] stack;
    private int top = -1;

    /**
     * 构造器
     *
     * @param maxSize
     */
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        this.stack = new int[maxSize];
    }

    /**
     * 判读栈空
     *
     * @return
     */
    private boolean isEmpty() {
        return top == -1;
    }

    /**
     * 判断栈满
     *
     * @return
     */
    private boolean isFull() {
        return top == maxSize - 1;
    }

    /**
     * 入栈
     *
     * @param value
     */
    public void push(int value) {
        //判断栈满
        if (isFull()) {
            System.out.println("栈满了~~");
            return;
        } else {
            top++;
            stack[top] = value;
        }

    }

    /**
     * 出栈
     *
     * @return
     */
    public int pop() {
        //判断栈空
        if (isEmpty()) {
            throw new RuntimeException("栈空啦~没有数据");
        } else {
            int result = stack[top];
            top--;
            return result;
        }
    }

    /**
     * 遍历栈,因为只能在栈顶操作,所以从栈顶开始显示数据
     */
    public void show(){
        //判断为空
        if(isEmpty()){
            System.out.println("栈空,没有数据");
            return;
        }
        for(int i = top; i > -1; i--){
            System.out.println(stack[i]);
        }
    }
}

二、综合计算器实现

1、实现支持加减乘除+整数的计算器
在这里插入图片描述

代码实现的过程需要注意大于9的数字即长度超过2的数字!!!

import java.util.Stack;

/**
 * 7*2*2-5+1-5+3-4=18
 * @author chengxn
 *
 */
public class Calculator2 {
    public static void main(String[] args) {
        String express = "13+2*16-2";
        Stack numStack = new Stack();
        Stack operStack = new Stack();

        int index = 0;
        char ch;
        String keepnum = "";
        while (true) {
            ch = express.substring(index, index + 1).charAt(0);
            if (Calculator2.isOper(ch)) {
                //如果是操作符
                if (!operStack.empty()) {
                    //不为空,判断运算符的优先级
                    if (Calculator2.priority(ch) <= Calculator2.priority((Character) operStack.peek())) {
                        //pop出两个数和一个运算符,计算结果,并入栈
                        int result = Calculator2.cal((Integer) numStack.pop(), (Integer) numStack.pop(), (Character) operStack.pop());
                        numStack.push(result);
                    }
                }
                operStack.push(ch);
            } else {
                //如果是数字,直接入栈 1的ascii码值是49
                //代码实现的过程需要注意大于9的数字即长度超过2的数字
               //  numStack.push(ch-48);  若不考虑长度大于2的数字,这样写就支持了

                keepnum += ch;
                //判断是不是最后一位,
                //是最后一位,直接入数栈
                //不是最后一位要看下一位是不是数字
                if (index == express.length() - 1) {
                    numStack.push(Integer.valueOf(keepnum));
                } else {
                    //下一位是操作符,则入栈,并且清空keepnum,如果是数字,则继续循字符串
                    if (Calculator2.isOper(express.substring(index + 1, index + 2).charAt(0))) {
                        numStack.push(Integer.valueOf(keepnum));
                        keepnum = "";
                    }
                }

            }
            index++;
            if (index >= express.length()) {
                break;
            }
        }

        //顺序从数栈和符号栈取值进行计算
        while (true) {
            //符号栈为空,就结束计算
            if (operStack.empty()) {
                break;
            }
            int result = Calculator2.cal((Integer) numStack.pop(), (Integer) numStack.pop(), (Character) operStack.pop());
            numStack.push(result);
        }

        System.out.println(express + "=" + numStack.peek());
    }


    /**
     * 判断运算符优先级
     *
     * @param oper
     * @return
     */
    private static int priority(char oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 0;
        } else {
            return -1;
        }
    }

    /**
     * 判断是否为运算符
     *
     * @param oper
     * @return
     */
    private static boolean isOper(char oper) {
        return oper == '*' || oper == '/' || oper == '+' || oper == '-';
    }

    /**
     * 计算
     *
     * @param num1
     * @param num2
     * @param oper
     * @return
     */
    private static int cal(int num1, int num2, char oper) {
        int result = 0;
        switch (oper) {
            case '*':
                result = num2 * num1;
                break;
            case '/':
                result = num2 / num1;
                break;
            case '+':
                result = num2 + num1;
                break;
            case '-':
                result = num2 - num1;
                break;
            default:
                break;
        }
        return result;
    }
}

三、前缀表达式&中缀表达式&后缀表达式

1)前缀表达式(波兰表达式)
运算符位于操作数之前
例如:(3+4)x5-6 对应的前缀表达式:- x + 3 4 5 6

前缀表达式计算机求值:
从右至左扫描表达式,遇到数字进栈,遇到运算符,弹出栈顶两个数,对他们做相应的运算,然后结果入栈,重复上述过程,直到表达式的最左端,最后的结果即为表达式的结果。

例如:(3+4)x5-6 前缀表达式:- x + 3 4 5 6
1、从右至左,6 5 4 3 入栈
2、遇到+,弹出3、4,计算得7入栈
3、遇到x,弹出7、5,计算得35入栈
4、遇到-,弹出35、6,计算得29为最终结果(最顶元素-次顶元素)

2)中缀表达式
就是我们常见的表达式
例如:(3+4)x5-6

对我们人来说是最熟悉的,但是对计算机来说不好操作,根据我们之前那个案例可以看出来,比如符号优先级,需要我们去判断,才能做运算。因此在计算结果时,一般是将中缀表达式转换为其他表达式(一般是后缀表达式)。

3)后缀表达式(逆波兰表达式)
运算符位于操作数之后
例如:(3+4)x5-6 对应的后缀表达式:3 4 + 5 x 6 -

后缀表达式计算机求值:
从左至右扫描表达式,遇到数字进栈,遇到运算符,弹出栈顶两个数,对他们做相应的运算,然后结果入栈,重复上述过程,直到表达式的最右端,最后的结果即为表达式的结果。

例如:(3+4)x5-6 后缀表达式: 3 4 + 5 x 6 –
1、从左至右,3 4入栈
2、遇到+,弹出4、3,计算得7入栈
3、遇到5,入栈
4、遇到x,弹出5、7,计算得35入栈
5、遇到6,入栈
6、遇到-,弹出6、35,计算得29为最终结果(次顶元素-最顶元素)

四、完成一个逆波兰计算器:支持小括号和整数

思路:
1、中缀表达式转换为后缀表达式
2、按照后缀表达式的规则计算

中缀表达式转后缀表达式思路
中缀转后缀: (3+4)x5-6
1、索引index,从左往右,有两个栈,S1符号栈,S2中间结果栈
2、(1)如果是数字,直接入S2
(2)运算符,
a.S1为空/ S1栈顶为‘(’/优先级高于栈顶运算符,进S1
b.优先级小于等于栈顶运算符,栈顶运算符弹出进S2,继续进行(2)的判断
(3)如果四括号,
a.左括号,进S1
b.右括号,S1运算符依次弹出进S2,遇到左括号,将括号丢弃
3、按2将表达式遍历结束后,将S1依次弹出进S2
4、S2从栈底到栈顶即为后缀表达式结果

整个代码实现:

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

/**
 * 逆波兰计算器
 * (3+4)*5-6
 *
 * @author chengxn
 */
public class PolandNotation {
    public static void main(String[] args) {
        String suffixExpression = "3 4 + 5 * 6 -";
        List<String> strList = Arrays.asList(suffixExpression.split(" "));
        Integer result = calculate(strList);
        System.out.println("结果为:" + result);


//        List<String> strList = turn("(3+4)*5-6");
//        System.out.println("后缀表达式:" + strList);
//        Integer result = calculate(strList);
//        System.out.println("结果为:" + result);


    }

    /**
     * 计算后缀表达式
     *
     * @param strList
     * @return
     */
    private static Integer calculate(List<String> strList) {
        Stack<String> stack = new Stack();
        for (String s : strList) {
            if (s.matches("\\d+")) {
                stack.push(s);
            } else {
                //pop出两个数并运算入栈
                int num = cal(Integer.parseInt(stack.pop()), Integer.parseInt(stack.pop()), s);
                stack.push(num + "");
            }
        }

        return Integer.parseInt(stack.peek());
    }

    private static int cal(int num1, int num2, String oper) {
        int result;
        switch (oper) {
            case "*":
                result = num2 * num1;
                break;
            case "/":
                result = num2 / num1;
                break;
            case "+":
                result = num2 + num1;
                break;
            case "-":
                result = num2 - num1;
                break;
            default:
                throw new RuntimeException("运算符有误");
        }
        return result;
    }


    /**
     * 中缀表达式转后缀表达式
     *
     * @param expression
     * @return
     */
    private static List<String> turn(String expression) {
        expression = "(3+4)*5-6";
        List<String> stringList = getExpressList(expression);
        //s1符号栈 s2中间结果栈 因为后面s2并没pop操作,所以使用list
        Stack<String> s1 = new Stack<>();
        List<String> s2 = new ArrayList<>();
        for (String s : stringList) {
            //如果是数,入s2
            if (s.matches("\\d+")) {
                s2.add(s);
            } else if (s.equals("(")) {
                //左括号直接入s1
                s1.push(s);
            } else if (s.equals(")")) {
                //如果是右括号,依次弹出s1的值进入s2,直到遇见左括号,将括号丢弃
                while (!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop(); //丢弃左括号
            } else {
                // S1为空/'('/优先级高于栈顶运算符,进S1
                //优先级小于等于栈顶运算符,栈顶运算符弹出进S2,继续进行判断
                while (!s1.empty() && !s1.peek().equals("(") && Operation.getValue(s) < Operation.getValue(s1.peek())) {
                    s2.add(s1.pop());
                }
                s1.push(s);
            }
        }

        //将s1依次弹出后入s2
        while (!s1.empty()) {
            s2.add(s1.pop());
        }
        return s2;
    }


    /**
     * 中缀表达式转为list
     *
     * @param expression
     * @return
     */
    private static List<String> getExpressList(String expression) {
        List<String> stringList = new ArrayList<>();
        int index = 0;
        String keepNum;
        char c;
        do {
            //如果是非数字,就直接放入list
            if ((c = expression.charAt(index)) < 48 || (c = expression.charAt(index)) > 57) {
                stringList.add(c + "");
                index++;
            } else {
                //数字,需要考虑多位数情况
                keepNum = "";
                while (index < expression.length() && (c = expression.charAt(index)) >= 48 && (c = expression.charAt(index)) <= 57) {
                    keepNum += c;
                    index++;
                }
                stringList.add(keepNum);
            }
        } while (index < expression.length());
        return stringList;
    }


}

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 result = 0;
        switch (operation) {
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                System.out.println("运算符不存在");
                break;
        }
        return result;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值