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

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

特征

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

图解

数据结构学习4-栈的使用、中缀表达式求值、中缀表达式转后缀表达式_第1张图片

代码实现

使用数组实现

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));
    }
}

你可能感兴趣的:(基础知识,java,数据结构)