20200913 第 5 章 栈

第 5 章 栈

5.1 栈的一个实际需求

请输入一个表达式

计算式:[722-5+1-5+3-3] 点击计算

请问: 计算机底层是如何运算得到结果的? 注意不是简单的把算式列出运算,因为我们看这个算式 7 * 2 * 2 - 5, 但是计算机怎么理解这个算式的(对计算机而言, 它接收到的就是一个字符串), 我们讨论的是这个问题。 -> 栈

5.2 栈的介绍

  1. 栈的英文为 Stack
  2. 栈是一个先入后出 (FILO-First In Last Out) 的有序列表。
  3. 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。 允许插入和删除的一端, 为变化的一端, 称为栈顶(Top), 另一端为固定的一端, 称为栈底(Bottom)。
  4. 根据栈的定义可知, 最先放入栈中元素在栈底, 最后放入的元素在栈顶, 而删除元素刚好相反, 最后放入的元素最先删除, 最先放入的元素最后删除
  5. 图解方式说明 出栈(pop)入栈(push) 的概念

5.3 栈的应用场景

  1. 子程序的调用: 在跳往子程序前, 会先将下个指令的地址存到堆栈中, 直到子程序执行完后再将地址取出, 以回到原来的程序中。
  2. 处理递归调用: 和子程序的调用类似, 只是除了储存下一个指令的地址外, 也将参数、 区域变量等数据存入堆栈中。
  3. 表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)。
  4. 二叉树的遍历。
  5. 图形的深度优先(depth-first)搜索法。

5.4 栈的快速入门

  1. 用数组模拟栈的使用, 由于栈是一种有序列表, 当然可以使用数组的结构来储存栈的数据内容,下面我们就用数组模拟栈的出栈, 入栈等操作。

  2. 实现思路分析,并画出示意图

实现 栈的 思路分析

img

  1. 使用数组来模拟栈
  2. 定义一个 top 来表示栈顶,初始化 为 -1
  3. 入栈的操作,当有数据加入到栈时, top++; stack[top] = data;
  4. 出栈的操作, int value = stack[top]; top--, return value

代码实现

public class ArrayStack {
    private int[] data; // 存储栈中数据
    private int maxSize; // 栈大小
    private int top = -1; // 栈顶索引,初始化为 -1

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        this.data = new int[maxSize];
    }

    public boolean isFull() {
        return top == maxSize - 1;
    }

    public boolean isEmpty() {
        return top == -1;
    }

    public void push(int item) {
        if (isFull()) {
            System.out.println("栈满,无法入栈");
            return;
        }
        data[++top] = item;
    }

    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空,无法出栈");
        }
        return data[top--];
    }

    public void list() {
        for (int i = top; i >= 0; i--) {
            System.out.printf("Stack[%d] = %d\n", i, data[i]);
        }
    }

    public static void main(String[] args) {
        //测试一下ArrayStack 是否正确
        //先创建一个ArrayStack对象->表示栈
        ArrayStack stack = new ArrayStack(4);
        String key = "";
        boolean loop = true; //控制是否退出菜单
        Scanner scanner = new Scanner(System.in);

        while(loop) {
            System.out.println("show: 表示显示栈");
            System.out.println("exit: 退出程序");
            System.out.println("push: 表示添加数据到栈(入栈)");
            System.out.println("pop: 表示从栈取出数据(出栈)");
            System.out.println("请输入你的选择");
            key = scanner.next();
            switch (key) {
                case "show":
                    stack.list();
                    break;
                case "push":
                    System.out.println("请输入一个数");
                    int value = scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    try {
                        int res = stack.pop();
                        System.out.printf("出栈的数据是 %d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }

        System.out.println("程序退出~~~");
    }
}

链表实现栈

节点类:

@Data
public class StackNode {
    private int data;
    private StackNode next; // 指向下一个节点

    public StackNode(int data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "StackNode{" + "data=" + data + '}';
    }
}

链表实现类:

public class LinkedListStack {
    private StackNode head = new StackNode(-1);
    private StackNode top = head; // 栈顶节点

    private boolean isEmpty() {
        return top == head;
    }

    public void push(StackNode node) {
        top.setNext(node);
        top = node;
    }

    public StackNode pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空,无法出栈");
        }
        StackNode res = top;
        // 找到最后一个节点
        StackNode cur = head;
        while (true) {
            if (cur.getNext() == top) {
                top = cur;
                top.setNext(null);
                break;
            }
            cur = cur.getNext();
        }

        return res;
    }

    public void list() {
        Stack<StackNode> stack = new Stack<>();
        StackNode cur = head.getNext();
        while (true) {
            if (cur == null) {
                break;
            }
            stack.push(cur);
            cur = cur.getNext();
        }

        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }

    public static void main(String[] args) {
        //测试一下ArrayStack 是否正确
        //先创建一个ArrayStack对象->表示栈
        LinkedListStack stack = new LinkedListStack();
        String key = "";
        boolean loop = true; //控制是否退出菜单
        Scanner scanner = new Scanner(System.in);

        while(loop) {
            System.out.println("show: 表示显示栈");
            System.out.println("exit: 退出程序");
            System.out.println("push: 表示添加数据到栈(入栈)");
            System.out.println("pop: 表示从栈取出数据(出栈)");
            System.out.println("请输入你的选择");
            key = scanner.next();
            switch (key) {
                case "show":
                    stack.list();
                    break;
                case "push":
                    System.out.println("请输入一个数");
                    int value = scanner.nextInt();
                    stack.push(new StackNode(value));
                    break;
                case "pop":
                    try {
                        StackNode res = stack.pop();
                        System.out.printf("出栈的数据是 %d\n", res.getData());
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }

        System.out.println("程序退出~~~");
    }
}

5.5 栈实现综合计算器(中缀表达式)

使用栈来实现综合计算器,输入表达式,计算结果,例如,输入 3+2*6-2 ,返回 13

思路分析

  1. 通过一个 index 值(索引),来遍历我们的表达式
  2. 如果我们发现是一个数字, 就直接入数栈
  3. 如果发现扫描到是一个符号, 就分如下情况
  4. 如果发现当前的符号栈为 空,就直接入栈
  5. 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
  6. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
  7. 最后在数栈只有一个数字,就是表达式的结果

修改后的思路

在老师的思路上进行了一些修改,修改后支持:

  • 表达式中存在括号
  • 表达式中存在多位数

总体思路为:

对括号中的表达式进行优先处理,处理后将结果数组替换到表达式中,并去掉括号。将所有括号去掉后,计算剩下的表达式;

计算表达式的思路为:

  1. 将字符串分为数和操作符两类,分别放入两个数组,并定义两个栈
  2. 循环处理操作符数组,每次遍历前将数放入到数栈
    1. 如果发现当前的符号栈为 空,就直接入栈
    2. 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,需要从数栈中 pop 出两个数,再从符号栈中 pop 出一个符号,进行运算,将得到结果,压入数栈,将当前的操作符入符号栈
    3. 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
  3. 当表达式扫描完毕,就顺序的从 数栈和符号栈中 pop 出相应的数和符号,并运行.
  4. 最后在数栈只有一个数字,就是表达式的结果

代码实现

增加了一些功能的栈:

public class ArrayStack {
    private int[] data; // 存储栈中数据
    private int maxSize; // 栈大小
    private int top = -1; // 栈顶索引,初始化为 -1

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        this.data = new int[maxSize];
    }

    public int size() {
        return top + 1;
    }

    public boolean isFull() {
        return top == maxSize - 1;
    }

    public boolean isEmpty() {
        return top == -1;
    }

    public void push(int item) {
        if (isFull()) {
            System.out.println("栈满,无法入栈");
            return;
        }
        data[++top] = item;
    }

    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("栈空,无法获取");
        }

        return data[top];
    }

    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空,无法出栈");
        }
        return data[top--];
    }

    public void list() {
        for (int i = top; i >= 0; i--) {
            System.out.printf("Stack[%d] = %d\n", i, data[i]);
        }
    }

}

简单计算器实现:

/**
 * 计算四则运算
 * <p>
 * 使用栈完成表达式的计算 思路
 * 1. 通过一个 index  值(索引),来遍历我们的表达式
 * 2. 如果我们发现是一个数字, 就直接入数栈
 * 3. 如果发现扫描到是一个符号,  就分如下情况
 * 3.1 如果发现当前的符号栈为 空,就直接入栈
 * 3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
 * 4. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
 * 5. 最后在数栈只有一个数字,就是表达式的结果
 * <p>
 * 验证: 3+2*6-2 = 13
 */
public class SimpleCalculator {

    public static void main(String[] args) {
        // String str = "3+2*6-2"; // 13
        String str = "(3+2)*(6-2)"; // 13

        // System.out.println(str);

        // 对 () 的特殊处理
        // 将 () 优先计算,然后替换字符串
        while (true) {

            int left = str.indexOf('(');
            int right = str.indexOf(')');
            if (left == -1) {
                break;
            }
            String subStr = str.substring(left + 1, right);
            // System.out.println(subStr);
            int result = calcStr(subStr);
            str = str.substring(0, left) + result + str.substring(right + 1);
        }

        int result = calcStr(str);

        System.out.println("结果是:" + result);

    }

    private static int calcStr(String calcStr) {
        // 1. 将字符串分为数和操作符两类
        String[] split = calcStr.split("[0-9]+");
        String[] operStrArr = Arrays.copyOfRange(split, 1, split.length); // 排除开始的空格
        String[] numArr = calcStr.split("[+|\\-|*|/]");
        int[] operArr = Stream.of(operStrArr).mapToInt(s -> s.charAt(0)).toArray();

        // System.out.println("operArr = " + Arrays.toString(operArr));
        // System.out.println("numArr = " + Arrays.toString(numArr));

        ArrayStack numStack = new ArrayStack(20);
        ArrayStack operStack = new ArrayStack(20);

        // 2. 将数和操作符分别压入栈中
        /*
         * * 2. 如果我们发现是一个数字, 就直接入数栈
         * 3. 如果发现扫描到是一个符号,  就分如下情况
         * 3.1 如果发现当前的符号栈为 空,就直接入栈
         * 3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,
         * 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,
         * 然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
         */

        int count = 0;

        while (true) {
            // 数先入数栈
            numStack.push(Integer.valueOf(numArr[count]));

            if (count >= operArr.length) {
                break;
            }

            if (operStack.isEmpty()) {
                operStack.push(operArr[count]);
            } else {
                if (getOperPriority(operArr[count]) <= getOperPriority(operStack.peek())) {
                    // 如果当前的操作符的优先级小于或者等于栈中的操作符,
                    // 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,
                    // 将得到结果,入数栈,然后将当前的操作符入符号栈
                    int num1 = numStack.pop();
                    int num2 = numStack.pop();
                    int oper = operStack.pop();
                    int result = calc(num1, num2, oper);
                    numStack.push(result);
                    operStack.push(operArr[count]);
                } else {
                    // 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
                    operStack.push(operArr[count]);
                }
            }

            count++;
        }

        // 4. 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.


        while (true) {
            if (operStack.isEmpty()) {
                break;
            }
            int num1 = numStack.pop();
            int num2 = numStack.pop();
            int oper = operStack.pop();
            int result = calc(num1, num2, oper);
            numStack.push(result);
        }

        // 5. 最后在数栈只有一个数字,就是表达式的结果
        return numStack.pop();
    }

    private static int getOperPriority(int oper) {

        int result = 0;

        if (oper == '*' || oper == '/') {
            result = 2;
        }

        if (oper == '+' || oper == '-') {
            result = 1;
        }

        return result;
    }

    private static int calc(int num1, int num2, int oper) {
        int result = 0;

        switch (oper) {
            case '+': {
                result = num1 + num2;
                break;
            }
            case '-': {
                result = num2 - num1;
                break;
            }
            case '*': {
                result = num1 * num2;
                break;
            }
            case '/': {
                result = num2 / num1;
                break;
            }
            default:
                break;
        }

        return result;
    }
}

5.6 逆波兰计算器

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

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

前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前

举例说明: (3+4)×5-6 对应的前缀表达式就是 - x + 3 4 5 6

前缀表达式的计算机求值

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

例如: (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,由此得出最终结果

中缀表达式

中缀表达式就是常见的运算表达式,如(3+4)×5-6

中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作(前面我们讲的案例就能看的这个问题),因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式.)

后缀表达式

后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后

中举例说明: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 –

再比如:

正常的表达式 逆波兰表达式
a+b a b +
a+(b-c) a b c - +
a+(b-c)*d a b c – d * +
a+d*(b-c) a d b c - * +
a=1+3 a 1 3 + =
后缀表达式的计算机求值

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

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

  1. 从左至右扫描,将3和4压入堆栈;
  2. 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
  3. 将5入栈;
  4. 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
  5. 将6入栈;
  6. 最后是 - 运算符,计算出35-6的值,即29,由此得出最终结果

5.6 逆波兰计算器

我们完成一个逆波兰计算器, 要求完成如下任务:

  1. 输入一个逆波兰表达式(后缀表达式), 使用栈(Stack), 计算其结果

  2. 支持小括号和多位数整数, 因为这里我们主要讲的是数据结构, 因此计算器进行简化, 只支持对整数的计算。

  3. 思路分析

    例如: (3+4)× 5-6 对应的后缀表达式就是 3 4 + 5 × 6 - , 针对后缀表达式求值步骤如下:
    1. 从左至右扫描, 将 3 和 4 压入堆栈;
    2. 遇到+运算符, 因此弹出 4 和 3(4 为栈顶元素, 3 为次顶元素) , 计算出 3+4 的值, 得 7, 再将 7 入栈;
    3. 将 5 入栈;
    4. 接下来是× 运算符, 因此弹出 5 和 7, 计算出 7× 5=35, 将 35 入栈;
    5. 将 6 入栈;
    6. 最后是-运算符, 计算出 35-6 的值, 即 29, 由此得出最终结果
    
  4. 代码完成

    /**
     * 使用逆波兰表达式计算简单四则运算
     */
    public class NPLCalculator {
        public static void main(String[] args) {
            // (3+4)× 5-6 = 29  =>   3 4 + 5 * 6 -
            // 4 * 5 - 8 + 60 + 8 / 2 = 76  =>  4 5 * 8 - 60 + 8 2 / +
            String npl = "4 5 * 8 - 60 + 8 2 / +";
    
            // 将逆波兰表达式放入 List
            List<String> list = Arrays.asList(npl.split(" "));
    
            // 计算逆波兰表达式
            Stack<String> stack = new Stack<>();
            for (String item : list) {
                if (item.matches("\\d+")) {
                    // 如果是数字,直接放入栈
                    stack.push(item);
                } else {
                    // 如果是操作符,从栈中 pop 出两个数字,然后与操作符进行计算,之后将结果压入栈中
                    int num1 = Integer.valueOf(stack.pop());
                    int num2 = Integer.valueOf(stack.pop());
                    int result = 0;
    
                    if (item.equals("+")) {
                        result = num1 + num2;
                    } else if (item.equals("-")) {
                        result = num2 - num1;
                    } else if (item.equals("*")) {
                        result = num1 * num2;
                    } else if (item.equals("/")) {
                        result = num2 / num1;
                    } else {
                        throw new RuntimeException("非四则运算符号");
                    }
                    stack.push(result + "");
                }
            }
    
            // 最后栈中剩下的就是结果
            System.out.println("结果是:" + stack.pop());
        }
    }
    

5.7 中缀表达式转换为后缀表达式

5.7.1 具体步骤

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

5.7.2 举例说明

将中缀表达式 1+((2+3)*4)-5 转换为后缀表达式的过程如下

img

因此结果为 : 1 2 3 + 4 * + 5 –

5.7.3 代码实现中缀表达式转为后缀表达式

/**
 * 使用逆波兰表达式计算简单四则运算
 */
public class NPLCalculator {

    public static void main(String[] args) {
        // 中缀表达式转后缀表达式
        String expression = "1+((2+3)*4)-5";

        // 将 中缀表达式转成对应的List
        List<String> strList = toStrList(expression);
        // System.out.println(strList);// [1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]

        // 将中缀表达式转为后缀表达式
        List<String> sufStrList = trans2SufList(strList);
        System.out.println(sufStrList); //  [1, 2, 3, +, 4, *, 5, -, +]

        System.out.println(calcRPN(sufStrList));    // 16

        // (3+4)× 5-6 = 29  =>   3 4 + 5 * 6 -
        // 4 * 5 - 8 + 60 + 8 / 2 = 76  =>  4 5 * 8 - 60 + 8 2 / +
        // String npl = "4 5 * 8 - 60 + 8 2 / +";
        //
        // int result = calcRPN(npl);
        // System.out.println(result);


    }

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

        for (String s : strList) {
            if (s.matches("\\d+")) {
                s2.add(s);
            } else if (s.equals("(")) {
                s1.push(s);
            } else if (s.equals(")")) {
                while (!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();
            } else {
                // 若优先级比栈顶运算符的高,也将运算符压入s1
                // 否则,将s1栈顶的运算符弹出并压入到s2中
                while (s1.size() != 0 && getPriority(s1.peek()) >= getPriority(s)) {
                    s2.add(s1.pop());
                }
                s1.add(s);
            }
        }

        // 将s1中剩余的运算符依次弹出并压入s2
        while (s1.size() > 0) {
            s2.add(s1.pop());
        }

        return s2;
    }

    private static int getPriority(String oper) {
        int res = 0;
        switch (oper) {
            case "+":
                res = 1;
                break;
            case "-":
                res = 1;
                break;
            case "*":
                res = 2;
                break;
            case "/":
                res = 2;
                break;
            default:
                System.out.println("不支持此运算法");
                break;
        }
        return res;
    }

    private static List<String> toStrList(String expression) {
        // 1+((2+3)*4)-5
        List<String> strList = new ArrayList<>();
        int i = 0; // 遍历表达式的索引
        char c;
        String str;
        while (i < expression.length()) {
            c = expression.charAt(i);
            if (c < '0' || c > '9') {
                // 不是数字,直接加入 List
                strList.add(c + "");
                i++;
            } else {
                str = "";
                while (i < expression.length() && !((c = expression.charAt(i)) < '0' || (c = expression.charAt(i)) > '9')) {
                    str += c;
                    i++;
                }
                strList.add(str);
            }
        }

        return strList;
    }

    private static int calcRPN(List<String> list) {
        // 计算逆波兰表达式
        Stack<String> stack = new Stack<>();
        for (String item : list) {
            if (item.matches("\\d+")) {
                // 如果是数字,直接放入栈
                stack.push(item);
            } else {
                // 如果是操作符,从栈中 pop 出两个数字,然后与操作符进行计算,之后将结果压入栈中
                int num1 = Integer.valueOf(stack.pop());
                int num2 = Integer.valueOf(stack.pop());
                int result = 0;

                if (item.equals("+")) {
                    result = num1 + num2;
                } else if (item.equals("-")) {
                    result = num2 - num1;
                } else if (item.equals("*")) {
                    result = num1 * num2;
                } else if (item.equals("/")) {
                    result = num2 / num1;
                } else {
                    throw new RuntimeException("非四则运算符号");
                }
                stack.push(result + "");
            }
        }

        // 最后栈中剩下的就是结果
        // System.out.println("结果是:" + stack.pop());
        return Integer.parseInt(stack.pop());
    }

    private static int calcRPN(String npl) {
        // 将逆波兰表达式放入 List
        List<String> list = Arrays.asList(npl.split(" "));

        return calcRPN(list);
    }
}
posted @ 2020-09-13 11:08  流星<。)#)))≦  阅读(401)  评论(0编辑  收藏  举报