学习栈,这一篇就足够了

每天最幸福的事情,莫过于下班回家后,踏进厨房,做上一道自己喜欢的菜肴,然后饱餐一顿。
起锅,烧油,葱姜蒜……
一顿操作猛如虎,一顿美味要出炉。
打开放厨具的柜子,从一摞碗当中拿出了最上边的一个,将饭菜盛出,开始饕餮大餐。

今天要讲的数据结构,就是这一摞碗——栈。

什么是栈

栈和队列有着相似之处,两者都是受着一定规则约束的线性数据结构,不同的是,队列遵循的是先进先出(FIFO)的规则,而栈坚持着后来者居上,遵循着后进先出(LIFO)的规则。
就像是我们用碗时,最后刷完的碗,都会摞在最顶上,而我们使用碗的时候,都会习惯性的从最上边取,这里用碗的时候就遵循这栈的规则,越是后来放进去的碗,越早被使用。

栈的实现方式

栈同队列一样,他的数据存储同样可以基于数组或者链表进行实现。
同样,我们使用接口来定义栈需要具备的方法,一般来说,栈必须具备入栈(push)和弹栈(pop)这两个操作的。

声明栈的必备方法:

/**
 * algorithm
 * @author 忘忧
 * @date 2020/3/15
 * 栈(公众号:算法之灵魂拷问)
 */
public interface Stack {

    /**
     * 获取当前栈的元素个数
     * @return 当前栈的元素个数
     */
    int size();

    /**
     * 判断当前栈是否已满
     * @return 当前栈是否已满
     */
    boolean isFull();

    /**
     * 弹栈
     * @return 出栈元素
     * @throws StackEmptyException 栈为空时抛出该异常
     */
    Object pop() throws StackEmptyException;

    /**
     * 元素入栈
     * @param element 入栈元素
     * @throws StackFullException 当栈已满时抛出该异常
     */
    void push(Object element) throws StackFullException;
}

基于数组的实现

思路: 栈的存储相对简单,使用一个指针标示当前栈顶位置,当元素入栈时,指针先加一再将新元素复制给当前位置;弹栈时,先获取到当前位置的值,然后指针减一。
注:指针初始化位置为 -1

代码实现:

/**
 * algorithm
 * @author 忘忧
 * @date 2020/3/15
 * 基于数组实现的栈(公众号:算法之灵魂拷问)
 */
public class ArrayStack implements Stack {

    /**
     * 栈内数据的具体存储位置
     */
    private Object[] elements;

    /**
     * 当前栈顶位置
     */
    private int currentIndex = -1;

    /**
     * 栈的容量
     * @param capacity 容量
     */
    public ArrayStack(int capacity) {
        this.elements = new Object[capacity];
    }

    /**
     * 当前栈存储的元素个数
     * @return
     */
    @Override
    public int size() {
        return currentIndex + 1;
    }

    /**
     * 判断当前栈是否已满
     * @return 是否已满
     */
    @Override
    public boolean isFull() {
        return this.size() == elements.length;
    }

    /**
     * 弹栈
     * @return 弹栈元素
     * @throws StackEmptyException 当栈为空时抛出异常
     */
    @Override
    public Object pop() throws StackEmptyException {
        if(this.size() == 0){
            throw new StackEmptyException();
        }
        return elements[currentIndex--];
    }

    /**
     * 入栈
     * @param element 入栈元素
     * @throws StackFullException 栈已满时抛出该异常
     */
    @Override
    public void push(Object element) throws StackFullException {
        if(this.isFull()){
            throw new StackFullException();
        }
        elements[++currentIndex] = element;
    }
}

基于链表实现的栈

思路:元素入栈,新元素做队首元素,原队首元素作为心入栈元素的next节点;元素出栈,栈顶元素的next节点做栈顶元素。

代码实现:

/**
 * algorithm
 * @author 忘忧
 * @date 2020/3/15
 * 基于链表实现的栈(公众号:算法之灵魂拷问)
 */
public class LinkedStack implements Stack {

    static class TreeNode{
        Object val;
        TreeNode next;
    }

    /**
     * 当前队首节点(栈顶元素)
     */
    private TreeNode head;

    /**
     * 当前栈存储的元素个数
     */
    private int size;

    /**
     * 当前栈存储的元素个数
     * @return 当前栈存储的元素个数
     */
    @Override
    public int size() {
        return size;
    }

    /**
     * 判断当前栈是否已满
     * @return 是否已满
     */
    @Override
    public boolean isFull() {
        return false;
    }

    /**
     * 弹栈
     * @return 栈顶元素
     * @throws StackEmptyException 栈空时抛出异常
     */
    @Override
    public Object pop() throws StackEmptyException {
        if(this.size() == 0){
            throw new StackEmptyException();
        }
        size--;
        Object result = head.val;
        head = head.next;
        return result;
    }

    /**
     * 元素入栈
     * @param element 入栈元素
     * @throws StackFullException 栈满时抛出异常
     */
    @Override
    public void push(Object element) throws StackFullException {
        TreeNode treeNode = new TreeNode();
        treeNode.val = element;
        treeNode.next = head;
        head = treeNode;
        size++;
    }
}

关于栈的常见面试题

如何用两个栈实现一个队列

结合队列和栈的相关知识,已经明了,队列是一种先进先出的数据结构,而栈则是一种先进后出的数据结构,那么怎么使用两个栈实现一个队列呢。
思路: 两个栈分别记录为栈A和栈B,要实现队列,需要从队列的入队和出队来入手。入队:元素直接入栈A出队:从栈B弹栈,当栈B为空时,弹出栈A的元素,放入栈B中(注:此过程会发生栈的倒置,即本来在栈A最低的元素,会出现在栈B的顶部),然后弹出栈B的栈顶元素即可,如果当前的栈B仍然是空,则说明整个队列为空,出队失败。

关于栈和队列,忘忧暂且先整理这么些,等忘忧将数据结构的基本知识整理完之后,会专门从leetcode上找一些相关的算法,在解题过程中,与大家一起加强对这些数据结构的理解。

感谢大家的关注,大家的关注将会成为忘忧最大的动力,忘忧也随时欢迎大家给我留言交流,一起成长!

结束语:沉舟侧畔千帆过,病树前头万木春。

发布了11 篇原创文章 · 获赞 170 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/u013054715/article/details/104872443