[java]-Algorithms and Data Structures-Chapter 5-Stack

5. Stack Stack

1. The actual needs of the stack

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-nu1BZd2P-1649582859474)(https://secure2.wostatic.cn/static/cne8Ts5vSxmfUG59GtyoD/image.png)]

2. Introduction to the stack

  1. The English of the stack is (stack)
  2. The stack is an ordered list of FILOfirst In Last Out.
  3. Stack (stack) is a special linear table that restricts the insertion and deletion of elements in the linear table to only be performed at the same end of the linear table. The end that allows insertion and deletion is the changing end, called the top of the stack (Top), and the other end is the fixed end, called the bottom of the stack (Bottom).
  4. According to the definition of the stack, the first element to be placed in the stack is at the bottom of the stack, the last element is at the top of the stack, and the
    deletion element is just the opposite, the last element is deleted first, and the first element is deleted last
  5. The concept of pop (pop) and push (push)

insert image description here

3. Application scenarios of the stack

  1. Subroutine call: Before jumping to the subroutine, the address of the next instruction will be stored in the stack, and the address will be taken out after the subroutine is executed to return to the original program. return
  2. Handle recursive calls: Similar to subroutine calls, except that in addition to storing the address of the next instruction, data such as parameters and area variables are also stored in the stack.
  3. Conversion and evaluation of expressions (actual resolution). [Infix expression —> Postfix expression]
  4. Traversal of a binary tree.
  5. Graph depth-first (depth-first) search method.

4. Analysis of stack ideas

1) Analysis

  1. Use an array to simulate a stack
  2. Define a top to represent the top of the stack, initialized to -1
  3. The operation of pushing into the stack, when there is data added to the stack, top++; stack[top] = data;
  4. The operation of popping the stack, int value = stack[top]; top–, return value

2) Array simulation stack

Test Methods

public static void testArrayStack(){
    
    
        ArrayStack arrayStack = 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.nextLine();
            switch (key){
    
    
                case "show":{
    
    
                    arrayStack.list();
                    break;
                }
                case "exit":{
    
    
                    loop = false;
                    break;
                }
                case "push":{
    
    
                    System.out.print("请输入一个数:");
                    int num = scanner.nextInt();
                    arrayStack.push(num);
                    System.out.println("-----成功啦!!!----------");
                    break;
                }
                case "pop":{
    
    
                    try {
    
    
                        System.out.println("获取数据:"+arrayStack.pop());
                    }catch (Exception e){
    
    
                        System.out.println(e.getMessage());
                    }
                }
                default:break;
            }
        }
    }
// 定义一个类,表示栈结构
class ArrayStack {
    
    

    private int maxSize;
    private int[] stack;
    private int top; // 初始索引

    public ArrayStack(int maxSize) {
    
    
        top = -1;
        this.maxSize = maxSize;
        stack = new int[maxSize];
    }

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

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

    // 添加
    public void push(int num) {
    
    
        if (isFull()) {
    
    
            System.out.println("------栈满-------");
            return;
        }
        top++;
        stack[top] = num;
    }

    // 取出
    public int pop() {
    
    
        if (isEmpty()) {
    
    
            throw new RuntimeException("------栈空啦------");
        }
        return stack[top--];
    }

    // 遍历栈
    public void list(){
    
    
        if(isEmpty()){
    
    
            System.out.println("-----栈空,没有数据------");
            return;
        }

        for (int i = top; i > -1; i--) {
    
    
            System.out.println("stack["+i+"]:"+stack[i]);
        }
    }
}

3) Linked list simulation stack

class Node {
    
    

    Node next;
    int id;

    public Node(int id, Node next) {
    
    
        this.id = id;
        this.next = next;
    }

    @Override
    public String toString() {
    
    
        return "Node{" +
                "id=" + id +
                '}';
    }
}

class LinkedStack {
    
    

    Node top;
    int maxSize;
    int count;

    public LinkedStack(int maxSize) {
    
    
        top = null;
        this.maxSize = maxSize;
    }

    public boolean isFull() {
    
    
        return count == maxSize;
    }

    public boolean isEmpty() {
    
    
        return count == 0;
    }

    public void push(int id) {
    
    
        if (isFull()) {
    
    
            System.out.println("---栈满了---");
            return;
        }
        top = new Node(id, top);
        count++;
    }

    public Node pop() {
    
    
        if (isEmpty()) {
    
    
            throw new RuntimeException("---栈空----");
        }
        Node node = top;
        top = top.next;
        count--;
        return node;
    }
}

5. Stack Implementation Calculator

1) Analysis of train of thought

If you don't understand, you can try to draw it yourself, and I don't understand it either.

insert image description here

2) Code implementation

To quote this classmate, what I think is different from the teacher, and the code code will be given to realize the calculator in the replay

6. Expressions

1) Prefix expression (Polish expression)

Prefix expressions are also called Polish expressions, and the operators of prefix expressions are placed before the operands.

For example: (3+4)×5-6the corresponding prefix expression is- x + 3 4 5 6

The computer value of the prefix expression :

Scan the expression from right to left, when a number is encountered, the number is pushed onto the stack, when an operator is encountered, the two numbers on the top of the stack are popped, and the operator is used to perform corresponding calculations on them (the top element and the next top element ), and push the result onto the stack; repeat the above process until the leftmost end of the expression, and the value obtained from the final operation is the result of the expression

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

2) Infix expressions

  1. Infix expressions are common arithmetic expressions, such as(3+4)×5-6
  2. The evaluation of infix expressions is the most familiar to us, but it is not easy for computers to operate (this problem can be seen in the case we talked about earlier), so when calculating the result, the infix expression is often used Expressions are transformed into other expressions for operation (generally into suffix expressions.)

3) Postfix expression

1. 后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
2. 举例说明: `(3+4)×5-6` 对应的后缀表达式就是 `3 4 + 5 × 6 –`
3. 比如
regular expression Reverse Polish Expressions
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 + =

Computer Evaluation of Postfix Expressions

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

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

7. Reverse Polish Calculator

public class Test04_逆波兰计算器 {
    
    
    public static void main(String[] args) {
    
    
        // 逆波兰表达式   (3+4)×5-6 --> 3 4 + 5 × 6 –
        // 4 * 5 - 8 + 60 + 8 / 2 --> 4 5 * 8 - 60 + 8 2 / +
        String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";
        // 1. 将 表达式放入 arrayList中
        // 2. 将 ArrayList传递给一个方法,遍历ArrayList 配合栈,完成计算
        List<String> listString = getListString(suffixExpression);
        int calculate = calculate(listString);
        System.out.println(calculate);

    }

    // 将一个逆波兰表达式,依次将数据和运算符放到 arrayList中
    public static List<String> getListString(String suffixExpression) {
    
    
        return Arrays.stream(suffixExpression.split(" ")).collect(Collectors.toList());
    }

    // 计算
    public static int calculate(List<String> ls) {
    
    
        // 1. 创建栈
        Stack<String> stack = new Stack<>();
        // 2. 遍历 ls
        ls.stream().forEach(item -> {
    
    
            // 3. 正则表示式取出数
            if (item.matches("\\d+")) {
    
     // 匹配多位数
                // 入栈
                stack.push(item);
            } else {
    
    
                // pop出两个数,并运算,结果入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")) {
    
    
                    res = num1 + num2;
                } else if (item.equals("-")) {
    
    
                    res = num1 - num2;
                } else if (item.equals("*")) {
    
    
                    res = num1 * num2;
                } else if (item.equals("/")) {
    
    
                    res = num1 / num2;
                } else {
    
    
                    throw new RuntimeException("符号错误");
                }
                // 把res 入栈
                stack.push(String.valueOf(res));
            }

        });
        // 最后留在stack中的数据就是结果
        return Integer.parseInt(stack.pop());
    }

}

8. Convert infix expression to postfix expression

1) Steps

  1. Initialize two stacks: operator stack s1 and stack s2 for storing intermediate results;
  2. Infix expressions are scanned from left to right;
  3. When an operand is encountered, press it to s2;
  4. When an operator is encountered, compare its priority with the operator at the top of the s1 stack:
    1. If s1 is empty, or the operator at the top of the stack is an left parenthesis (, then directly push this operator onto the stack;
    2. Otherwise, if the priority is higher than that of the operator at the top of the stack, the operator is also pushed into s1;
    3. Otherwise, pop the operator at the top of the stack in s1 and push it into s2, go to (4. 1) again and compare it with the new operator at the top of the stack in s1;
  5. When parentheses are encountered:
    1. If it is a left parenthesis (, press it directly into s1
    2. If it is a right parenthesis ), the operator on the top of the stack of s1 will be popped one by one, and pushed into s2 until the left parenthesis is encountered, and the pair of parentheses will be discarded at this time
  6. Repeat steps 2 to 5 until the far right of the expression
  7. Pop the remaining operators in s1 sequentially and push them into s2
  8. The elements in s2 are popped up one by one and output, and the reverse order of the result is the postfix expression corresponding to the infix expression

insert image description here

public class Test04_逆波兰计算器 {
    
    
    public static void main(String[] args) {
    
    
    /*    // 逆波兰表达式   (3+4)×5-6 --> 3 4 + 5 × 6 –
        // 4 * 5 - 8 + 60 + 8 / 2 --> 4 5 * 8 - 60 + 8 2 / +
        String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";
        // 1. 将 表达式放入 arrayList中
        // 2. 将 ArrayList传递给一个方法,遍历ArrayList 配合栈,完成计算
        List<String> listString = getListString(suffixExpression);
        int calculate = calculate(listString);
        System.out.println(calculate);*/

        test1();

    }

    // 中缀表达式转 后缀表达式zs
    // 1. 1 + ( ( 2 + 3 ) * 4 ) -5
    // 2. 先将表达式转为 中缀
    // 3. 中缀转后缀
    public static void test1() {
    
    
        String suffixExpression = "1+((2+3)*4)-5";
        List<String> strings = toInfixExpressionList(suffixExpression);
        System.out.println(strings);
        List<String> strings1 = parseSuffixExpression(strings);
        System.out.println(strings1);
        System.out.println(calculate(strings1));

    }

    // 中缀表达式 --> 转 list
    public static List<String> toInfixExpressionList(String s) {
    
    
        List<String> list = new ArrayList<>();
        int i = 0; // 指针,用于遍历 s
        String str; // 对多位数进行拼接
        char c; // 每遍历一个字符,就放入到 c
        do {
    
    
            // 如果 c是非数字,
            if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57) {
    
    
                list.add("" + c);
                i++;
            } else {
    
    
                // 如果是一个数,需要考虑多位数问题
                str = "";
                while (i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57) {
    
    
                    str += c;
                    i++;
                }
                list.add(str);
            }
        }
        while (i < s.length());
        return list;
    }

    // list ---> 后缀表达式
    public static List<String> parseSuffixExpression(List<String> list) {
    
    
        // 1. 定义两个栈
        Stack<String> s1 = new Stack<>(); // 符号栈
        // 因为 s2 整个转换过程中 , 没有 pop操作, 后面需要逆序输出, 因此使用 ArrayList替代
//        Stack<String> s2 = new Stack<>(); // 中间结果栈
        List<String> s2 = new ArrayList<>();

        // 2. 遍历 list
        list.stream().forEach(item -> {
    
    
            // 如果是 数 , 进入 s2
            if (item.matches("\\d+")) {
    
    
                s2.add(item);
            } else if (item.equals("(")) {
    
    
                s1.push(item);
            } else if (item.equals(")")) {
    
    
                // 依次弹出 s1 顶的运算符,压入 s2,直到(,删除()
                while (!s1.peek().equals("(")) {
    
    
                    s2.add(s1.pop());
                }
                s1.pop(); // 弹出 (
            } else {
    
    
                // 当 item 的优先级小于 或等于栈顶运算符的优先级
                // 将 s1 栈顶的运算符弹出压入 s2 中,
                // 再次与 s1 栈顶的运算符进行比较
                while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
    
    
                    s2.add(s1.pop());
                }
                // item 压入栈
                s1.push(item);
            }
        });

        // 3. 将 s1 剩余的 运算符加入 s2
        while (s1.size() != 0){
    
    
            s2.add(s1.pop());
        }
        return s2;

    }

    // 将一个逆波兰表达式,依次将数据和运算符放到 arrayList中
    public static List<String> getListString(String suffixExpression) {
    
    
        return Arrays.stream(suffixExpression.split(" ")).collect(Collectors.toList());
    }

    // 计算
    public static int calculate(List<String> ls) {
    
    
        // 1. 创建栈
        Stack<String> stack = new Stack<>();
        // 2. 遍历 ls
        ls.stream().forEach(item -> {
    
    
            // 3. 正则表示式取出数
            if (item.matches("\\d+")) {
    
     // 匹配多位数
                // 入栈
                stack.push(item);
            } else {
    
    
                // pop出两个数,并运算,结果入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")) {
    
    
                    res = num1 + num2;
                } else if (item.equals("-")) {
    
    
                    res = num1 - num2;
                } else if (item.equals("*")) {
    
    
                    res = num1 * num2;
                } else if (item.equals("/")) {
    
    
                    res = num1 / num2;
                } else {
    
    
                    throw new RuntimeException("符号错误");
                }
                // 把res 入栈
                stack.push(String.valueOf(res));
            }

        });
        // 最后留在stack中的数据就是结果
        return Integer.parseInt(stack.pop());
    }

}

// 类 ,返回运算符的优先级
class Operation {
    
    
    static int ADD = 1;
    static int SUB = 1;
    static int MUL = 2;
    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:break;
        }
        return result;
    }
}

Guess you like

Origin blog.csdn.net/m0_56186460/article/details/124225150
Recommended