Data structure and algorithm -- Application of stack

Definition of stack

Stack is a linear structure with limited operation (first in and last out). It is only allowed to enter and exit from one end. This is the characteristic of the stack. Generally, the end of data in and out is called the top of the stack, and the other end is the bottom of the stack. The basic operations of the stack include entering the stack, leaving the stack, empty judgment, taking the top elements, etc.

How to implement a stack

Stack can be realized by array, which mainly includes two basic methods: entering stack and exiting stack. The stack class provided for us in Java is the most basic stack structure.

public
class Stack<E> extends Vector<E> {
    /**
     * Creates an empty Stack.
     */
    public Stack() {
    }

    /**
     * Pushes an item onto the top of this stack. This has exactly
     * the same effect as:
     * <blockquote><pre>
     * addElement(item)</pre></blockquote>
     *
     * @param   item   the item to be pushed onto this stack.
     * @return  the <code>item</code> argument.
     * @see     java.util.Vector#addElement
     */
    public E push(E item) {
        addElement(item);

        return item;
    }

    /**
     * Removes the object at the top of this stack and returns that
     * object as the value of this function.
     *
     * @return  The object at the top of this stack (the last item
     *          of the <tt>Vector</tt> object).
     * @throws  EmptyStackException  if this stack is empty.
     */
    public synchronized E pop() {
        E       obj;
        int     len = size();

        obj = peek();
        removeElementAt(len - 1);

        return obj;
    }

    /**
     * Looks at the object at the top of this stack without removing it
     * from the stack.
     *
     * @return  the object at the top of this stack (the last item
     *          of the <tt>Vector</tt> object).
     * @throws  EmptyStackException  if this stack is empty.
     */
    public synchronized E peek() {
        int     len = size();

        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }

    /**
     * Tests if this stack is empty.
     *
     * @return  <code>true</code> if and only if this stack contains
     *          no items; <code>false</code> otherwise.
     */
    public boolean empty() {
        return size() == 0;
    }

    /**
     * Returns the 1-based position where an object is on this stack.
     * If the object <tt>o</tt> occurs as an item in this stack, this
     * method returns the distance from the top of the stack of the
     * occurrence nearest the top of the stack; the topmost item on the
     * stack is considered to be at distance <tt>1</tt>. The <tt>equals</tt>
     * method is used to compare <tt>o</tt> to the
     * items in this stack.
     *
     * @param   o   the desired object.
     * @return  the 1-based position from the top of the stack where
     *          the object is located; the return value <code>-1</code>
     *          indicates that the object is not on the stack.
     */
    public synchronized int search(Object o) {
        int i = lastIndexOf(o);

        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = 1224463164541339165L;
}

Use an array to implement a stack

public class MyStack {

    // Used to save data in the stack
    private int[] elementData;

    // The number of current elements in the stack
    private int elementCount;

    // Stack size
    private int size;

    public MyStack(int size) {
        this.size = size;
        this.elementCount = 0;
        this.elementData = new int[size];
    }

    public void push(int val) throws Exception {
        if (elementCount == size) {
            throw new Exception("The stack is full");
        }
        elementData[elementCount] = val;
        elementCount++;
    }

    public int pop() throws Exception {
        if (elementCount == 0) {
            throw new Exception("The stack is empty");
        }
        return elementData[--elementCount];
    }

}

It can be seen that the implementation of a stack is still very simple. The stack provided by JDK additionally supports dynamic capacity expansion, which is actually the basic operation of the array.

Time complexity of stack

Obviously, in a fixed size stack, the time complexity of out stack and in stack is O(1). For a stack that supports dynamic capacity expansion, the time complexity of in stack may involve array capacity expansion, so it is also O(n). However, most operations will not involve array capacity expansion, so it is close to O(1) in terms of average time complexity.

Common problems of stack

Two questions from leetcode

1. Minimum stack

Design a stack that supports push, pop and top operations and can retrieve the smallest element in a constant time.

/**
 * Two stacks are used. One stack completes the normal in and out operation, and the top of the other stack always stores the smallest element in the current stack
 */
public class MinStack {

    Stack<Integer> stack = new Stack();
    Stack<Integer> minStack = new Stack();

    /**
     * If the minStack stack is empty or the element at the top of the stack is less than val, add an element at the top of the stack repeatedly
     * @param val
     */
    public void push(int val) {
        stack.push(val);
        if (!minStack.isEmpty() && val > minStack.peek()) {
            minStack.push(minStack.peek());
        } else {
            minStack.push(val);
        }
    }

    public void pop() {
        stack.pop();
        minStack.pop();

    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

2. Valid parentheses

A given only includes '(',')','{','}','[',']' String of s ,Determine whether the string is valid.

Valid strings must meet:

The left parenthesis must be closed with the same type of right parenthesis.
The left parentheses must be closed in the correct order.
 

Example 1:

Input: s = "()"
Output: true
 Example 2:

Input: s = "()[]{}"
Output: true
 Example 3:

Input: s = "(]"
Output: false
 Example 4:

Input: s = "([)]"
Output: false
 Example 5:

Input: s = "{[]}"
Output: true
public class ValidParentheses {

    static Map<Character, Character> map = new HashMap<Character, Character>() {{
        put(')', '(');
        put(']', '[');
        put('}', '{');
    }};

    public boolean isValid(String s) {
        Stack<Character> stack = new Stack();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (map.containsKey(c)) {
                if (stack.isEmpty() || stack.peek() != map.get(c)) {
                    return false;
                } else {
                    stack.pop();
                }
            }else{
                stack.push(c);
            }

        }
        return stack.isEmpty();
    }

}

3. Implement queue with stack

One stack cannot complete the queue. We can realize this problem through two stacks. One stack is dedicated to adding elements and the other stack is dedicated to taking out elements.

Suppose that the stack of added elements is: A, and the stack of elements is: B.
Every time the data is push ed into stack A, when pop is needed, if stack B is empty, all the data used for stack A will be ejected and added to stack B at one time, otherwise it will be ejected directly from stack B, which realizes the operation of queue.

Suppose you want to add three elements: 1, 2 and 3

Now you need to pop up an element. First, judge whether stack B is empty. At this time, stack B is empty. Then, pop up all the data of stack A to stack B at one time.

Then, an element pops up from the B stack, which is 1 (1 is advanced and out first, which conforms to the queue structure).

At this time, if another element 4 needs to be added, continue to add to the A stack.

When another element needs to pop up, because stack B is not empty, it will pop up directly from stack B.

import java.util.Stack;

public class Code_StackImplQueue<E> {
    Stack<E> stack_a = new Stack<>();
    Stack<E> stack_b = new Stack<>();

    /**
     * Each time the element is pushed, it is pushed to the stack_a stack
     *
     * @param e
     */
    public void push(E e) {
        stack_a.push(e);
    }

    public E pop() {
        /**
         * If stack_ If B is empty, stack is required_ All elements of a pop up and push to the stack_b medium
         */
        if (stack_b.empty()) {
            while (!stack_a.empty()) {
                stack_b.push(stack_a.pop());
            }
        }
        //If stack_ If B is still empty, it means stack_ If a is also empty, null will be returned directly
        if (stack_b.empty()) {
            return null;
        }
        //From stack_b take out the element
        return stack_b.pop();
    }
}

Tags: Algorithm data structure

Posted on Wed, 20 Oct 2021 20:08:40 -0400 by TowerOfPower