数据结构-4-栈

四、栈

1、概念

栈和队列类似,不同的是栈遵循 LIFOlast-in-first-out)的原则,即最后一个入栈的元素,最先被取出

2、常用方法

  1. push。入栈
  2. pop。出栈
  3. peek。看一下栈顶的元素

3、示例

【1】数组栈

class ArrayStack<T> {
    
    
    private final int length;
    private Object[] array;
    private int top; // 指向栈顶的前一个位置

    public ArrayStack(int length) {
    
    
        this.length = length;
        this.array = new Object[this.length];
        this.top = -1;
    }

    public void push(T element) {
    
    
        if (isFull()) {
    
    
            return;
        }

        array[++top] = element;
    }

    @SuppressWarnings("unchecked")
    public T pop() {
    
    
        if (isEmpty()) {
    
    
            return null;
        }

        return (T)array[top--];
    }

    @SuppressWarnings("unchecked")
    public T peek() {
    
    
        if (isEmpty()) {
    
    
            return null;
        }

        return (T)array[top];
    }

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

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

    @Override
    public String toString() {
    
    
        if (isEmpty()) {
    
    
            return "[]";
        }

        StringBuilder sBuilder = new StringBuilder("[");
        for (int i = top; i > 0; i--) {
    
    
            sBuilder.append(array[i]).append(',').append(' ');
        }
        sBuilder.append(array[0]).append(']');
        return sBuilder.toString();
    }

}

【2】链表栈

class LinkedStack<T> {
    
    
    private final int length;
    private Node<T> top;
    private int size;

    public LinkedStack(int length) {
    
    
        this.length = length;
        this.top = null;
        this.size = 0;
    }

    public void push(T element) {
    
    
        if (isFull()) {
    
    
            return;
        }

        Node<T> add = new Node<T>(element);
        add.next = top;
        top = add;
        this.size++;
    }

    public T pop() {
    
    
        if (isEmpty()) {
    
    
            return null;
        }

        Node<T> node = this.top;
        this.top = node.next;
        this.size--;
        return node.element;
    }

    public T peek() {
    
    
        if (isEmpty()) {
    
    
            return null;
        }

        return this.top.element;
    }

    public boolean isFull() {
    
    
        return size == length;
    }

    public boolean isEmpty() {
    
    
        return this.top == null;
    }

    private static class Node<T> {
    
    
        private T element;
        private Node<T> next;

        public Node(T element) {
    
    
            this.element = element;
        }

        @Override
        public String toString() {
    
    
            return (null != element) ? element.toString() : "null";
        }
    }

    @Override
    public String toString() {
    
    
        if (isEmpty()) {
    
    
            return "[]";
        }

        StringBuilder sBuilder = new StringBuilder("[");
        Node<T> node = this.top;
        while (true) {
    
    
            sBuilder.append(node.element);
            if (null == node.next) {
    
    
                break;
            }
            node = node.next;
            sBuilder.append(',').append(' ');
        }
        sBuilder.append(']');
        return sBuilder.toString();
    }

}

4、应用

【1】中缀表达式

中缀表达式即我们日常计算时使用的表达式算法,例如 1 + 2 * ( 3 / 4.5 )

即运算符在运算量的中间。虽然对于人类来说计算方便,但是对于计算机计算时却比较麻烦

计算方式:

  1. 使用2个栈结构,一个保存数字(数栈),另一个保存运算符(符号栈)

  2. 从左向右遍历中缀表达式,当遇到数字时,放到数栈中

  3. 当遇到运算符时,如果符号栈为空,则直接入符号栈

  4. 如果符号栈不为空,则需要判断符号栈顶的运算符和当前运算符的优先级(左小括号优先级别最低):

    如果当前运算符的优先级大于栈顶的符号,就把当前运算符放到符号栈中

    如果当前运算符的优先级小于或等于栈顶的符号,则取出栈顶符号,同时从数栈中取出两个数字,第一个数字作为 (加/减/乘/除)数 ,第二个数字作为 被(加/减/乘/除)数 ,将计算结果放到数栈中,同时将当前运算符入符号栈

  5. 当遇到小括号时,如果是左小括号,则直接入符号栈,如果是右小括号,则需要依次取出一个运算符和两个数字,计算后,将结果放到数栈中,重复此操作,直到遇到左括号,并将其出栈为止

  6. 最后:从符号栈中取出栈顶符号,同时从数栈中取出两个数字,第一个数字作为 (加/减/乘/除)数 ,第二个数字作为 被(加/减/乘/除)数 ,将计算结果放到数栈中,重复此操作,直到符号栈空为止。最终数栈会留下一个元素,即计算结果

示例:

import java.util.Stack;

class InfixExpression {
    
    

    public static double calc(String expression) {
    
    
        if (null == expression || 0 == expression.length()) {
    
    
            return 0;
        }

        // 替换空白字符,包括空格、制表符、换页符等
        expression = expression.replaceAll("\\s", "");
        if (0 == expression.length()) {
    
    
            return 0;
        }

        return split(expression);
    }

    private static double split(String expression) {
    
    
        Stack<String> digitStack = new Stack<String>();
        Stack<String> operatorStack = new Stack<String>();

        String digit = "";
        for (int i = 0, len = expression.length(); i < len; i++) {
    
    
            char ch = expression.charAt(i);
            if (isOperator(ch)) {
    
    
                if (!"".equals(digit)) {
    
     // 先判断是否有数字
                    digitStack.push(digit);
                    digit = "";
                }

                if (operatorStack.isEmpty()) {
    
     // 符号栈为空
                    operatorStack.push(String.valueOf(ch));
                } else {
    
    
                    if ('(' == ch) {
    
    
                        operatorStack.push(String.valueOf(ch));
                    } else if (')' == ch) {
    
    
                        char operator = operatorStack.pop().charAt(0);

                        while ('(' != operator) {
    
    
                            String digit1 = digitStack.pop();
                            String digit2 = digitStack.pop();
                            double calc = calc(digit1, digit2, operator);
                            digitStack.push(Double.toString(calc));

                            operator = operatorStack.pop().charAt(0);
                        }
                    } else {
    
    
                        char pop = operatorStack.pop().charAt(0);

                        // 当前运算符的算数优先级 大于 符号栈顶的运算符的算数优先级
                        if (getPriority(ch) > getPriority(pop)) {
    
    
                            operatorStack.push(String.valueOf(pop));
                        } else {
    
    
                            String digit1 = digitStack.pop();
                            String digit2 = digitStack.pop();
                            double calc = calc(digit1, digit2, pop);
                            digitStack.push(Double.toString(calc));
                        }

                        operatorStack.push(String.valueOf(ch));
                    }
                }

            } else {
    
    
                digit += ch;
            }
        }

        if (!"".equals(digit)) {
    
     // 再判断一次是否有数字
            digitStack.push(digit);
        }

        return result(digitStack, operatorStack);
    }

    /**
     * 加减乘除 和 小括号都是运算符
     */
    private static boolean isOperator(char ch) {
    
    
        return '+' == ch || '-' == ch || '*' == ch || '/' == ch || '(' == ch || ')' == ch;
    }

    private static double calc(String digit1, String digit2, char operator) {
    
    
        switch (operator) {
    
    
            case '+':
                return Double.valueOf(digit2) + Double.valueOf(digit1);
            case '-':
                return Double.valueOf(digit2) - Double.valueOf(digit1);
            case '*':
                return Double.valueOf(digit2) * Double.valueOf(digit1);
            case '/':
                return Double.valueOf(digit2) / Double.valueOf(digit1);
            default:
                return 0;
        }
    }

    private static int getPriority(char operator) {
    
    
        switch (operator) {
    
    
            case '(':
                return 0;
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            default:
                throw new IllegalArgumentException("运算符不正确!");
        }
    }

    private static double result(Stack<String> digitStack, Stack<String> operatorStack) {
    
    
        while (!operatorStack.isEmpty()) {
    
     // 符号栈不为空
            String digit1 = digitStack.pop();
            String digit2 = digitStack.pop();
            char operator = operatorStack.pop().charAt(0);
            double calc = calc(digit1, digit2, operator);
            digitStack.push(Double.toString(calc));
        }
        return Double.valueOf(digitStack.pop()); // 最终数栈只留下一个元素:计算结果
    }

}

【2】前缀表达式(波兰表达式)

前缀表达式即运算符在运算量的前面,例如:+ 1 * 2 / 3 4.5

中缀表达式转前缀表达式:

  1. 使用2个栈结构,一个保存数字(数栈),另一个保存运算符(符号栈)

  2. 从右向左遍历中缀表达式,当遇到数字时,放到数栈中

  3. 当遇到运算符时,如果符号栈为空,则直接入符号栈

  4. 如果符号栈不为空,则需要判断符号栈顶的运算符和当前运算符的优先级(右小括号优先级别最低):

    如果当前运算符的优先级大于或等于栈顶的符号,就把当前运算符放到符号栈中

    如果当前运算符的优先级小于栈顶的符号,则取出符号栈顶的符号,放入数栈中,同时将当前运算符入符号栈

  5. 当遇到小括号时,如果是右小括号,则直接入符号栈,如果是左小括号,则需要取出符号栈顶的符号,放入数栈中,重复此操作,直到遇到右括号,并将其出栈为止

  6. 最后将符号栈中的符号依次出栈,并入数栈中

计算方式:

  1. 反转数栈中的元素
  2. 使用1个临时栈结构
  3. 依次出栈波兰表达式中的元素,如果是数字则入临时栈
  4. 如果是符号,则从临时栈中取出两个数字,第一个数字作为 被(加/减/乘/除)数第二个数字作为 (加/减/乘/除)数 ,将计算结果放到临时栈中,重复此操作,直到所有元素都出栈为止。最终临时栈会留下一个元素,即计算结果

示例:

import java.util.Stack;

class PrefixExpression {
    
    

    public static double calc(String expression) {
    
    
        if (null == expression || 0 == expression.length()) {
    
    
            return 0;
        }

        expression = expression.replaceAll("\\s", "");
        if (0 == expression.length()) {
    
    
            return 0;
        }

        return split(expression);
    }

    private static double split(String expression) {
    
    
        Stack<String> digitStack = new Stack<String>();
        Stack<String> operatorStack = new Stack<String>();

        String digit = "";
        for (int i = expression.length() - 1; i >= 0; i--) {
    
     // 从右向左遍历
            char ch = expression.charAt(i);
            if (isOperator(ch)) {
    
    
                if (!"".equals(digit)) {
    
     // 先判断是否有数字
                    digitStack.push(digit);
                    digit = "";
                }

                if (operatorStack.isEmpty()) {
    
     // 符号栈为空
                    operatorStack.push(String.valueOf(ch));
                } else {
    
    
                    if (')' == ch) {
    
    
                        operatorStack.push(String.valueOf(ch));
                    } else if ('(' == ch) {
    
    
                        char operator = operatorStack.pop().charAt(0);

                        while (')' != operator) {
    
    
                            digitStack.push(String.valueOf(operator));
                            operator = operatorStack.pop().charAt(0);
                        }
                    } else {
    
    
                        char pop = operatorStack.pop().charAt(0);
                        if (getPriority(ch) >= getPriority(pop)) {
    
    
                            operatorStack.push(String.valueOf(pop));
                        } else {
    
    
                            digitStack.push(String.valueOf(pop));
                        }

                        operatorStack.push(String.valueOf(ch));
                    }
                }

            } else {
    
    
                digit = ch + digit; // 当前数字放到临时保存的数字前面
            }
        }

        if (!"".equals(digit)) {
    
     // 再判断一次是否有数字
            digitStack.push(digit);
        }

        while (!operatorStack.isEmpty()) {
    
     // 将运算符入到数栈中
            digitStack.push(operatorStack.pop());
        }

        return result(digitStack);
    }

    /**
     * 加减乘除 和 小括号都是运算符
     */
    private static boolean isOperator(char ch) {
    
    
        return '+' == ch || '-' == ch || '*' == ch || '/' == ch || '(' == ch || ')' == ch;
    }

    private static double calc(String digit1, String digit2, char operator) {
    
    
        switch (operator) {
    
    
            case '+':
                return Double.valueOf(digit1) + Double.valueOf(digit2);
            case '-':
                return Double.valueOf(digit1) - Double.valueOf(digit2);
            case '*':
                return Double.valueOf(digit1) * Double.valueOf(digit2);
            case '/':
                return Double.valueOf(digit1) / Double.valueOf(digit2);
            default:
                return 0;
        }
    }

    private static int getPriority(char operator) {
    
    
        switch (operator) {
    
    
            case ')':
                return 0;
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            default:
                throw new IllegalArgumentException("运算符不正确!");
        }
    }

    private static double result(Stack<String> digitStack) {
    
    
        printNotation(digitStack);

        Stack<String> temp = new Stack<String>();
        while (!digitStack.isEmpty()) {
    
     // 反转波兰表达式
            temp.push(digitStack.pop());
        }

        Stack<String> tempDigitStack = new Stack<String>();
        while (!temp.isEmpty()) {
    
    
            String pop = temp.pop();
            char first = pop.charAt(0);
            if (isOperator(first)) {
    
    
                String digit1 = tempDigitStack.pop();
                String digit2 = tempDigitStack.pop();
                double calc = calc(digit1, digit2, first);
                tempDigitStack.push(Double.toString(calc));
            } else {
    
    
                tempDigitStack.push(pop);
            }
        }

        return Double.valueOf(tempDigitStack.pop());
    }

    private static void printNotation(Stack<String> digitStack) {
    
    
        for (int i = digitStack.size() - 1; i >= 0; i--) {
    
    
            System.out.print(digitStack.get(i) + "  ");
        }
        System.out.println();
    }

}

【3】后缀表达式(逆波兰表达式)

逆波兰表达式(reverse Polish notation,RPN)把运算量写在前面,把运算符写在后面

例如:1 2 3 4.5 / * +

中缀表达式转逆波兰表达式:

  1. 使用2个栈结构,一个保存数字(数栈),另一个保存运算符(符号栈)

  2. 从左向右遍历中缀表达式,当遇到数字时,放到数栈中

  3. 当遇到运算符时,如果符号栈为空,则直接入符号栈

  4. 如果符号栈不为空,则需要判断符号栈顶的运算符和当前运算符的优先级(左小括号优先级别最低):

    如果当前运算符的优先级大于栈顶的符号,就把当前运算符放到符号栈中

    如果当前运算符的优先级小于或等于栈顶的符号,则取出符号栈顶的符号,放入数栈中,同时将当前运算符入符号栈

  5. 当遇到小括号时,如果是左小括号,则直接入符号栈,如果是右小括号,则需要取出符号栈顶的符号,放入数栈中,重复此操作,直到遇到左括号,并将其出栈为止

  6. 最后将符号栈中的符号依次出栈,并入数栈中

  7. 反转数栈中的元素

计算方式:

  1. 使用1个临时栈结构
  2. 依次出栈逆波兰表达式中的元素,如果是数字则入临时栈
  3. 如果是符号,则从临时栈中取出两个数字,第一个数字作为 (加/减/乘/除)数 ,第二个数字作为 被(加/减/乘/除)数 ,将计算结果放到临时栈中,重复此操作,直到所有元素都出栈为止。最终临时栈会留下一个元素,即计算结果

示例:

import java.util.Stack;

class RPN {
    
    

    public static double calc(String expression) {
    
    
        if (null == expression || 0 == expression.length()) {
    
    
            return 0;
        }

        expression = expression.replaceAll("\\s", "");
        if (0 == expression.length()) {
    
    
            return 0;
        }

        return split(expression);
    }

    private static double split(String expression) {
    
    
        Stack<String> digitStack = new Stack<String>();
        Stack<String> operatorStack = new Stack<String>();

        String digit = "";
        for (int i = 0, len = expression.length(); i < len; i++) {
    
    
            char ch = expression.charAt(i);
            if (isOperator(ch)) {
    
    
                if (!"".equals(digit)) {
    
     // 先判断是否有数字
                    digitStack.push(digit);
                    digit = "";
                }

                if (operatorStack.isEmpty()) {
    
     // 符号栈为空
                    operatorStack.push(String.valueOf(ch));
                } else {
    
    
                    if ('(' == ch) {
    
    
                        operatorStack.push(String.valueOf(ch));
                    } else if (')' == ch) {
    
    
                        char operator = operatorStack.pop().charAt(0);

                        while ('(' != operator) {
    
    
                            digitStack.push(String.valueOf(operator));
                            operator = operatorStack.pop().charAt(0);
                        }
                    } else {
    
    
                        char pop = operatorStack.pop().charAt(0);
                        if (getPriority(ch) > getPriority(pop)) {
    
    
                            operatorStack.push(String.valueOf(pop));
                        } else {
    
    
                            digitStack.push(String.valueOf(pop));
                        }

                        operatorStack.push(String.valueOf(ch));
                    }
                }

            } else {
    
    
                digit += ch;
            }
        }

        if (!"".equals(digit)) {
    
     // 再判断一次是否有数字
            digitStack.push(digit);
        }

        while (!operatorStack.isEmpty()) {
    
     // 将运算符入到数栈中
            digitStack.push(operatorStack.pop());
        }

        Stack<String> temp = new Stack<String>();
        while (!digitStack.isEmpty()) {
    
     // 反转数栈
            temp.push(digitStack.pop());
        }

        return result(temp);
    }

    /**
     * 加减乘除 和 小括号都是运算符
     */
    private static boolean isOperator(char ch) {
    
    
        return '+' == ch || '-' == ch || '*' == ch || '/' == ch || '(' == ch || ')' == ch;
    }

    private static double calc(String digit1, String digit2, char operator) {
    
    
        switch (operator) {
    
    
            case '+':
                return Double.valueOf(digit2) + Double.valueOf(digit1);
            case '-':
                return Double.valueOf(digit2) - Double.valueOf(digit1);
            case '*':
                return Double.valueOf(digit2) * Double.valueOf(digit1);
            case '/':
                return Double.valueOf(digit2) / Double.valueOf(digit1);
            default:
                return 0;
        }
    }

    private static int getPriority(char operator) {
    
    
        switch (operator) {
    
    
            case '(':
                return 0;
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            default:
                throw new IllegalArgumentException("运算符不正确!");
        }
    }

    private static double result(Stack<String> digitStack) {
    
    
        printNotation(digitStack);

        Stack<String> tempDigitStack = new Stack<String>();
        while (!digitStack.isEmpty()) {
    
    
            String pop = digitStack.pop();
            char first = pop.charAt(0);
            if (isOperator(first)) {
    
    
                String digit1 = tempDigitStack.pop();
                String digit2 = tempDigitStack.pop();
                double calc = calc(digit1, digit2, first);
                tempDigitStack.push(Double.toString(calc));
            } else {
    
    
                tempDigitStack.push(pop);
            }
        }

        return Double.valueOf(tempDigitStack.pop());
    }

    private static void printNotation(Stack<String> digitStack) {
    
    
        for (int i = digitStack.size() - 1; i >= 0; i--) {
    
    
            System.out.print(digitStack.get(i) + "  ");
        }
        System.out.println();
    }

}

猜你喜欢

转载自blog.csdn.net/adsl624153/article/details/103865915