数据结构与算法---栈的应用

栈的定义

栈是一种操作受限的(先进后出)线性结构,只允许从一端进出,这是栈的特点,一般数据进出的一端我们称为栈顶,另一端则为栈底,栈的基本操作包含进栈、出栈、判空、取栈顶元素等等。

如何实现一个栈

栈可以使用数组来实现,主要包含进栈和出栈两个基本方法,在Java中为我们提供的Stack类,就是最基本的栈结构。

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

自己用数组实现一个栈

public class MyStack {
    
    

    // 用来保存栈中数据
    private int[] elementData;

    // 栈中当前元素的数量
    private int elementCount;

    // 栈的大小
    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("栈已经满了");
        }
        elementData[elementCount] = val;
        elementCount++;
    }

    public int pop() throws Exception {
    
    
        if (elementCount == 0) {
    
    
            throw new Exception("栈是空的");
        }
        return elementData[--elementCount];
    }

}

可以看出实现一个栈还是非常简单的,JDK提供的栈额外支持动态扩容,实际上也是数组的基本操作。

栈的时间复杂度

很明显在固定大小的栈中,出栈和入栈都是O(1)的时间复杂度,而对于支持动态扩容的栈来说,入栈可能会涉及到数组的扩容,所以也是O(n)的时间复杂度,但是大多数的操作都不会涉及到数组的扩容,所以从平均时间复杂度来看,也是趋近于O(1)的。

栈的常见题目

来自leetcode上的两题

1、最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

/**
 * 使用两个栈,一个栈完成正常的进出操作,另一个栈的栈顶始终存放着当前栈中的最小元素
 */
public class MinStack {
    
    

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

    /**
     * 如果minStack栈为空,或者栈顶的元素小于val,则重复添加一个栈顶的元素
     * @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、有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
 

示例 1:

输入:s = "()"
输出:true
示例 2:

输入:s = "()[]{}"
输出:true
示例 3:

输入:s = "(]"
输出:false
示例 4:

输入:s = "([)]"
输出:false
示例 5:

输入:s = "{[]}"
输出: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、用栈实现队列

一个栈是无法完成队列的,本题我们可以通过两个栈来实现,一个栈专门用来添加元素,另一个栈专门用来取出元素。

假设分别设添加元素的栈为:A,取元素的栈为:B。
每次数据都push到A栈中,当需要pop时,如果B栈为空,则一次性把用来A栈数据全部弹出并添加到B栈,否则直接从B栈弹出,这样即实现了队列的操作。

假设现在要添加了3个元素,分别为:1,2,3
在这里插入图片描述
现在需要弹出一个元素,首先判断B栈是否为空,此时B栈是为空的,那么则一次性把A栈数据全部弹出到B栈。
在这里插入图片描述
然后从B栈弹出一个元素,就是1(1是先进的,现在也先出了,符合队列结构)。
在这里插入图片描述
此时如果又需要添加一个元素4,则继续向A栈添加。
在这里插入图片描述
当再又元素要弹出时,因为B栈不为空,所以直接从B栈弹出。
在这里插入图片描述

import java.util.Stack;

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

    /**
     * 每次push元素时都push到stack_a栈中
     *
     * @param e
     */
    public void push(E e) {
    
    
        stack_a.push(e);
    }

    public E pop() {
    
    
        /**
         * 如果stack_b为空,则需要把stack_a的元素全部弹出并push到stack_b中
         */
        if (stack_b.empty()) {
    
    
            while (!stack_a.empty()) {
    
    
                stack_b.push(stack_a.pop());
            }
        }
        //如果stack_b还为空,则表示stack_a也为空,则直接返回null
        if (stack_b.empty()) {
    
    
            return null;
        }
        //从stack_b取出元素
        return stack_b.pop();
    }
}

おすすめ

転載: blog.csdn.net/CSDN_WYL2016/article/details/120858569