数据结构学习4-栈的使用、中缀表达式求值、中缀表达式转后缀表达式

栈是数据结构中非常重要的一个知识点,java底层虚拟机运行就是通过栈的形式,编译器会将代码打包编译成为一个一个的栈帧 然后逐个执行。

特征

  • FILO 先进后出,最新进入的元素最后一个被弹出
  • 有序 栈是上一个有序列表
  • 插入和移除只能在同一端进行

图解

在这里插入图片描述

代码实现

使用数组实现
public class ArrayStack {
    /**
     * 最大容量
     */
    private int maxSize;
    /**
     * 栈顶位置
     */
    private int top = -1;
    /**
     * 存放数据的数组
     */
    private int[] stack;
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        // 初始化数组
        this.stack = new int[maxSize];
    }
    /**
     * 栈空
     */
    public boolean isEmpty() {
        return this.top == -1;
    }
    /**
     * 栈满
     */
    public boolean isFull() {
        return this.top == this.maxSize - 1;
    }
    /**
     * 放入数据 入栈
     */
    public void push(int val){
        if(isFull()){
            throw new RuntimeException("栈满-maxSize:"+maxSize);
        }
        stack[++top] = val;
    }
    /**
     * 弹出数据  出栈
     */
    public int pop(){
        if(isEmpty()){
            throw new RuntimeException("栈空...");
        }
        return stack[top--];
    }
    /**
     * 打印
     */
    public void print(){
        for(int i=top;i>=0;i--){
            System.out.printf("\tstack[%d]=%d\n",i,stack[i]);
        }
    }
    public static void main(String[] args) {
        ArrayStack stack = new ArrayStack(5);
        stack.push(1);
        stack.push(2);
        stack.print();
        System.out.println("pop:"+stack.pop());
        stack.print();
    }
}
使用链表实现
public class LinkedStack {
    private LinkedStackNode top;
    private int maxSize;
    private int size;
    public LinkedStack(int maxSize){
        this.maxSize = maxSize;
    }
    /**
     * 栈空
     */
    public boolean isEmpty(){
        return size == 0;
    }
    /**
     * 栈满
     */
    public boolean isFull(){
        return size == maxSize;
    }
    /**
     * 入栈
     */
    public void push(int val){
        if(isFull()){
            throw new RuntimeException("栈满:"+maxSize);
        }
        LinkedStackNode newTop = new LinkedStackNode(val);
        newTop.next = top;
        top = newTop;
        size ++;
    }
    /**
     * 出栈
     */
    public int pop(){
        if(isEmpty()){
            throw new RuntimeException("栈空...");
        }
        int val = top.val;
        top = top.next;
        size--;
        return val;
    }
    /**
     * 打印
     */
    public void print(){
        LinkedStackNode temp = top;
        while(temp != null){
            System.out.println(temp);
            temp = temp.next;
        }
    }
    static class LinkedStackNode{
        private int val;
        private LinkedStackNode next;
        public LinkedStackNode(int val){
            this.val = val;
        }
        @Override
        public String  toString() {
            return "LinkedStackNode{" +
                    "val=" + val +
                    '}';
        }
    }
    public static void main(String[] args) {
        LinkedStack stack = new LinkedStack(5);
        stack.push(1);
        stack.push(2);
        stack.print();
        System.out.println("pop:"+stack.pop());
        stack.print();
        System.out.println("pop:"+stack.pop());
//        System.out.println("pop:"+stack.pop());
        stack.push(1);
        stack.push(2);
        stack.push(1);
        stack.push(2);
        stack.push(1);
        stack.push(2);
    }
}

栈的应用

后面的应用都使用这个栈

public class CalculatorStack<T> {
    /**
     * 最大容量
     */
    private final int maxSize;
    /**
     * 栈顶位置
     */
    private int top = -1;
    /**
     * 存放数据的数组
     */
    private final Object[] stack;
    public CalculatorStack(int maxSize) {
        this.maxSize = maxSize;
        // 初始化数组
        this.stack = new Object[maxSize];
    }
    public boolean isEmpty() {
        return this.top == -1;
    }
    public boolean isFull() {
        return this.top == this.maxSize - 1;
    }
    public void push(T val) {
        if (isFull()) {
            throw new RuntimeException("栈满-maxSize:" + maxSize);
        }
        stack[++top] = val;
    }
    public T pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空...");
        }
        T val = (T) stack[top];
        stack[top] = null;
        top--;
        return val;
    }
    public T peek() {
        if (isEmpty()) {
            throw new RuntimeException("栈空...");
        }
        return (T) stack[top];
    }
    public void print() {
        for (int i = top; i >= 0; i--) {
            System.out.printf("\tstack[%d]=%s\n", i, stack[i]);
        }
    }
    public int size() {
        return top + 1;
    }
}
使用栈实现一个计算器
public class Calculator {

    /**
     * 数栈
     */
    private CalculatorStack<Integer> numStack;

    /**
     * 操作符栈
     */
    private CalculatorStack<Character> operStack;

    public Calculator() {
        numStack = new CalculatorStack(10);
        operStack = new CalculatorStack(10);
    }

    /**
     * 执行计算
     *
     * @param expression
     * @return
     */
    public int execute(String expression) {
        int index = 0;
        // 第一个值 第二个值
        int num1, num2, oper;
        String keepNum = "";
        int len = expression.length();
        while (index < len) {
            char c = expression.charAt(index);
            if (isOper(c)) {
                // 如果有多位数的话 就先给多位数入栈 再进行下面的处理
                if (!"".equals(keepNum)) {
                    numStack.push(Integer.parseInt(keepNum));
                    keepNum = "";
                }
                // 当前扫描到的是符号
                if (c == '(') {
                    // 当扫描到的是( 直接入符号栈 因为仅仅只是(并不能进行计算
                    operStack.push(c);
                } else if (c == ')') {
                    // 如果当前扫描到的是) 那么要循环处理符号栈和数字栈中的值,
                    // 循环结束条件 当数字栈只有一个数字 或者符号栈下一个值为(
                    while (!operStack.isEmpty()) {
                        // 要先判断符号栈是否满足继续往下的条件 否则可能出现栈空的情况
                        oper = operStack.pop();
                        if (oper == '(' || numStack.size() == 1) {
                            break;
                        }
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        int val = calculate(num1, num2, oper);
                        numStack.push(val);
                    }
                } else {
                    // 当符号栈有值并且优先级不低于当前的符号
                    // 这里一定要注意不低于 如果只判断大于的话 会导致 6 -7 +1 计算结果为 -2的情况
                    while (!operStack.isEmpty() && priority(operStack.peek()) >= priority(c)) {
                        // 从数值栈取出2个值 再取出符号栈中的值进行计算
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        int val = calculate(num1, num2, oper);
                        numStack.push(val);
                    }
                    // 符号直接入栈
                    operStack.push(c);
                }
            } else {
                // 数值可能是多位数 所以不能直接入栈
                keepNum += c;
            }
            index++;
        }
        // 将最后的值推入栈中 因为最后结束的时候没有符号,所以keepNum中还有最后一次的值
        if (!"".equals(keepNum)) {
            numStack.push(Integer.parseInt(keepNum));
        }
        // 计算栈内的数据
        // 每次计算的逻辑的都是从数栈中取出2个数 然后从操作栈中获取一个符号 然后将计算结果再压入数值栈
        // 直到符号栈为空
        while (!operStack.isEmpty()) {
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            int result = calculate(num1, num2, oper);
            numStack.push(result);
        }
        return numStack.pop();
    }

    /**
     * 判断当前的值是否为操作符
     *
     * @param val
     * @return
     */
    public boolean isOper(int val) {
        return val == '*' || val == '/' || val == '+' || val == '-' || val == '(' || val == ')';
    }

    /**
     * 判断优先级
     *
     * @param oper
     * @return
     */
    public int priority(int oper) {
        if (oper == '(' || oper == ')') {
            // 括号的优先级应该是最低的 括号不能被普通的运算消耗掉 只能通过特定的if去按组消除
            return -999;
        }
        if (oper == '*' || oper == '/') {
            return 1;
        }
        return 0;
    }

    /**
     * 执行计算
     *
     * @param a
     * @param b
     * @param oper
     * @return
     */
    public int calculate(int a, int b, int oper) {
        int result;
        switch (oper) {
            case '+': result = a + b; break;
            case '-': result = b - a; break;
            case '*': result = a * b; break;
            case '/': result = b / a; break;
            default:
                throw new RuntimeException("不支持的操作符:" + Character.toChars(oper));
        }
        return result;
    }
    public static void main(String[] args) {
        String expression = "2-5*2+8";// 0
        expression = "1+2-3*(4-2-1)+8";// 8
        expression = "(4-2-1)*3";// 3
        expression = "3*(4-2-1)";// 3

        expression = "1-2*3+4*((5/5+8-7*2)-3)";// -37
        Calculator calculator = new Calculator();
        System.out.println(expression + "=" + calculator.execute(expression));
    }
}
使用栈实现中缀表达式转后缀表达式
public class PolandNotation {
    private final CalculatorStack<Character> s1;
    private final CalculatorStack<String> s2;
    public PolandNotation() {
        s1 = new CalculatorStack<>(100);
        s2 = new CalculatorStack<>(100);
    }

    /**
     * 执行转换
     */
    public String execute(String expression) {
        int index = 0;
        // 第一个值 第二个值
        String keepNum = "";
        int len = expression.length();
        while (index < len) {
            char c = expression.charAt(index);
            if (isOper(c)) {
                if (!"".equals(keepNum)) {
                    // 操作数直接入s2
                    s2.push(keepNum);
                    keepNum = "";
                }
                // 如果当前操作符是( 直接压入s1
                if ('(' == c) {
                    s1.push(c);
                } else if (')' == c) {
                    // 如果当前符号是) 那么依次弹出s1中的符号直到 遇到(为之 , () 这组符号不入s2
                    Character pop = s1.pop();
                    while (pop != '(') {
                        s2.push(pop.toString());
                        pop = s1.pop();
                    }
                } else {
                    while (true) {
                        if (s1.isEmpty() || '(' == s1.peek()) {
                            // 当s1为空或者s1的栈顶为( 的时候 直接将当前符号压入s1
                            s1.push(c);
                            break;
                        } else if (priority(c) > priority(s1.peek())) {
                            // 如果当前操作符的优先级高于栈顶的操作运算符 那么也压入s1
                            // 注意 这里不能加等于
                            s1.push(c);
                            break;
                        } else {
                            // 将s1的栈顶弹出到s2中 然后再重新执行本循环
                            s2.push(s1.pop().toString());
                        }
                    }
                }
            } else {
                // 数值可能是多位数 所以不能直接入栈
                keepNum += c;
            }
            index++;
        }
        // 将最后一个数压入数栈
        if (!"".equals(keepNum)) {
            s2.push(keepNum);
        }
        // 将最后的操作符号压入栈中
        while (!s1.isEmpty()) {
            s2.push(s1.pop().toString());
        }
        StringBuilder result = new StringBuilder();
        while (!s2.isEmpty()) {
            result.append(s2.pop()).append(" ");
        }
        // 结果还需要反转
        return result.reverse().toString().trim();
    }

    /**
     * 判断当前的值是否为操作符
     */
    public boolean isOper(int val) {
        return val == '*' || val == '/' || val == '+' || val == '-' || val == '(' || val == ')';
    }

    /**
     * 判断优先级
     */
    public int priority(int oper) {
        if (oper == '(' || oper == ')') {
            // 括号的优先级应该是最低的 括号不能被普通的运算消耗掉 只能通过特定的if去按组消除
            return 999;
        }
        if (oper == '*' || oper == '/') {
            return 1;
        }
        return 0;
    }
    public static void main(String[] args) {
        String expression = "(1+2)*3";
        expression = "1+((2+3)*4)-5";
        PolandNotation polandNotation = new PolandNotation();
        System.out.println(expression + " <==> " + polandNotation.execute(expression));
    }
}
实现一个逆波兰计算器
public class PolandNotationCalculator {

    /**
     * 数栈
     */
    private CalculatorStack<String> numStack;

    public PolandNotationCalculator() {
        numStack = new CalculatorStack(10);
    }

    public int execute(String normalExpression){
        // 先将正常的表达式转换成为逆波兰表达式
       String polandNotationExpression = new PolandNotation().execute(normalExpression);
       return executePolandNotation(polandNotationExpression);
    }

    /**
     * 执行计算
     */
    public int executePolandNotation(String polandNotationExpression) {
        String[] ss = polandNotationExpression.split(" ");
        String num1, num2;
        for (int i = 0; i < ss.length; i++) {
            String cur = ss[i];
            if (isOper(cur)) {
                // 弹出2个数 和当前符号进行计算
                num1 = numStack.pop();
                num2 = numStack.pop();
                int result = calculate(num1, num2, cur);
                // 将结果再放入到数栈中
                numStack.push(result + "");
            } else {
                numStack.push(cur);
            }
        }
        // 弹出最后的值
        return Integer.parseInt(numStack.pop());
    }

    /**
     * 判断当前的值是否为操作符
     */
    public boolean isOper(String val) {
        return !Character.isDigit(val.charAt(0));
    }


    /**
     * 执行计算
     */
    public int calculate(String num1, String num2, String oper) {
        int a = Integer.parseInt(num1);
        int b = Integer.parseInt(num2);
        int result;
        switch (oper) {
            case "+": result = a + b; break;
            case "-": result = b - a; break;
            case "*": result = a * b; break;
            case "/": result = b / a; break;
            default:
                throw new RuntimeException("不支持的操作符:" + oper);
        }
        return result;
    }

    public static void main(String[] args) {
        String expression = "3 4 + 5 * 6 -";// (3 +4)*5 -6 = 29
        PolandNotationCalculator calculator = new PolandNotationCalculator();
        System.out.println(expression + "=" + calculator.executePolandNotation(expression));
        expression = "(3+4)*5-6";
        System.out.println(expression+"="+calculator.execute(expression));
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值