Refined solution to the bracket matching problem and ultimate stack design: uncovering the mysteries of the largest stack and the smallest stack

Directory

    • Bracket matching problem
    • Minimum stack
    • Maximum stack

Max stack and min stack are two important variants of extreme stack. The max stack is used to store the maximum value of the current match, and the min stack is used to store the minimum value of the current match.

Bracket matching problem

Let’s take a look at the description of Likou 20 questions:
Given a string s that only includes (’, )’, {’, }’, [’, ]’, determine whether the string is valid.

A valid string must satisfy:

  1. The opening bracket must be closed by a closing bracket of the same type.
  2. Opening brackets must be closed in the correct order.
  3. Every right bracket has a corresponding left bracket of the same type.

Example 1:
Input: s = “()”
Output: true
Example 2:
Input: s = “()[]{}”
Output: true
Example 3:
Input: s = “(]”
Output: false

We have two solutions to this problem:
1. We use a hash table to store all symbols first, with the left symbol as key and the right symbol as value. When traversing the string, when the symbol on the left is encountered, it is pushed onto the stack. When the symbol on the right is encountered, it is compared with the symbol on the top of the stack. If there is no match, false is returned.

 public boolean isValid(String s) {<!-- -->
        //Get the string length
        int n = s.length();
        //If the string length is an odd number, return false
        if (n % 2 == 1) {<!-- -->
            return false;
        }
        //Create a HashMap to store brackets in strings
        Map<Character, Character> map = new HashMap<>();
        map.put('[', ']');
        map.put('(', ')');
        map.put('{', '}');
        //Create a stack to store brackets in the string
        Stack<Character> stack = new Stack<>();
        //Loop through each character in the string
        for (int i = 0; i < s.length(); i + + ) {<!-- -->
            char item = s.charAt(i);
            //If the characters in the string exist in the HashMap, push them onto the stack
            if (map.containsKey(item)) {<!-- -->
                stack.push(item);
            } else {<!-- -->
                //If the stack is not empty, pop the top element of the stack. If the popped element does not match the characters in the current string, return false
                if (stack.isEmpty() == false) {<!-- -->
                    char pop = stack.pop();
                    if (map.get(pop) != item) {<!-- -->
                        return false;
                    }
                } else {<!-- -->
                    return false;
                }

            }

        }
        //If the stack is empty, return true, otherwise return false
        return stack.isEmpty();
    }
  1. Simply use the stack. If you encounter the symbol on the left, push it directly into the stack. When you encounter the symbol on the right, you will first determine whether the stack is empty. If it is empty, it will return false. If it is not empty, the top element of the stack will be popped. If the top element of the stack does not match, The symbol on the left directly returns false, and the last element is traversed and the return stack is empty.
public boolean isValid(String s) {<!-- -->
        int n = s.length();
        // If the string length is an odd number, return false directly
        if (n % 2 == 1) {<!-- -->
            return false;
        }
        //Create a stack
        Deque<Character> stack = new LinkedList<>();
        // Traverse the string
        for (int i = 0; i < s.length(); i + + ) {<!-- -->
            char c = s.charAt(i);
            // If the current character is a left bracket, push it onto the stack
            if (c == '(' | c == '[' || c == '{') {<!-- -->
                stack.push(c);
            // If the current character is a right bracket, pop an element from the stack. If the popped element does not match the current character, return false
            } else if (c == '}' || c == ']' || c == ')') {<!-- -->
                if (stack.isEmpty()) {<!-- -->
                    return false;
                }
                char top = stack.pop();
                if ((top != '(' & amp; & amp; c == ')') || (top != '{' & amp; & amp; c == '}') || (top != '[' & amp; & amp; c == ']')) {<!-- -->
                    return false;
                }
            }

        }
        //If the stack is empty, return true, otherwise return false
        return stack.isEmpty();
    }

Minimal stack

Let’s look at the description of Likou 155 question:
Design a stack that supports push, pop, and top operations and can retrieve the smallest element in constant time.

Implement the MinStack class:

  • MinStack() initializes the stack object.
  • void push(int val) Push element val onto the stack.
  • void pop() removes the element at the top of the stack.
  • int top() Gets the element at the top of the stack.
  • int getMin() Gets the smallest element in the stack.

The popular understanding of this question is to provide a method for the stack to obtain the smallest element in constant time.
We can design an auxiliary stack to insert and delete synchronously with the element stack to store the minimum value of each element when it is pushed into the stack (that is to say, in the auxiliary stack, what we insert every time is the minimum value in the element stack)

  • When an element is pushed onto the stack, we take the minimum value stored on the top of the current auxiliary stack and insert it into the auxiliary stack with the minimum value among the elements currently being pushed onto the stack.
  • When an element is to be popped from the stack, we also pop the top element of the auxiliary stack.
    Let’s look at the specific implementation code:
class MinStack {<!-- -->
    //Define two double-ended queues to store the input value and the current minimum value respectively.
    Deque<Integer> xStack;
    Deque<Integer> minStack;

    public MinStack() {<!-- -->
        //Initialize the double-ended queue
        xStack = new LinkedList<>();
        minStack = new LinkedList<>();
        //The first minimum value is set to the maximum value
        minStack.push(Integer.MAX_VALUE);
    }

    public void push(int val) {<!-- -->
        // Enter a value
        xStack.push(val);
        // Compare the current minimum value with the input value and take the smaller value
        minStack.push(Math.min(minStack.peek(), val));
    }

    public void pop() {<!-- -->
        // Pop the last value of the deque
        xStack.pop();
        minStack.pop();
    }

    public int top() {<!-- -->
        // Return the last value of the deque
        return xStack.peek();
    }

    public int getMin() {<!-- -->
        // Return the minimum value of the deque
        return minStack.peek();
    }
}

Max stack

Similar to the minimum stack implementation method, find the maximum value.
What needs attention is the last method, popping the maximum value, specifically getting the maximum element, then popping all the elements above the maximum value in the digital stack and storing them in the new stack, then popping the maximum value, and finally putting them in the new stack elements are pushed back onto the number stack.
Since Likou’s maximum stack is a VIP question, we can try Niuke’s maximum stack problem.

class MaxStack {<!-- -->
    //Define two stacks, one to store numbers and the other to store the maximum value
    Deque<Integer> xStack;
    Deque<Integer> maxStack;

    public MaxStack() {<!-- -->
        //Initialize two stacks
        xStack = new LinkedList<>();
        maxStack = new LinkedList<>();

    }

    public void push(int val) {<!-- -->
        // Get the current maximum value. If the stack is empty, the maximum value is the current value.
        int max = maxStack.isEmpty() ? val : maxStack.peek();
        // Compare the current value and the maximum value and take the maximum value
        max = max > val ? max : val;
        //Push the value and maximum value onto the stack respectively
        xStack.push(val);
        maxStack.push(max);
    }

    public int pop() {<!-- -->

        // Pop the top element of the maximum stack
        maxStack.pop();
        // Pop the top element of the number stack
        return xStack.pop();
    }

    public int top() {<!-- -->
        // Return the top element of the digital stack
        return xStack.peek();
    }

    public int peekMax() {<!-- -->
        //Return the top element of the maximum value stack
        return maxStack.peek();
    }

    public int popMax() {<!-- -->
        // Get the top element of the maximum value stack
        int max = peekMax();
        //Create a stack
        Stack<Integer> stack = new Stack<>();
        // When the top element of the stack is not equal to the maximum value, push the top element of the stack onto the stack
        while (top() != max) {<!-- -->
            stack.push(pop());
        }
        // Pop the top element of the digital stack
        pop();
        //Pop the elements from the stack and push them into the digital stack
        while (!stack.isEmpty()) {<!-- -->
            push(stack.pop());
        }
        // Return the maximum value
        return max;
    }

}