什么是栈?

本文将介绍一个重要的数据结构—栈,和以前讲到的链表数组同样也是一种数据呈线性排列的数据结构,不过在这种结构中,咱们只能访问最新添加的数据。栈就像是一摞书,拿到新书时咱们会把它放在书堆的最上面,取书时也只能从最上面的新书开始取。算法

如上就是栈的概念图,如今存储在栈中的只有数据 Blue。往栈中添加数据的时候,新数据被放在最上面。数组

而后,咱们往栈中添加了数据 Green。往栈中添加数据的操做叫做入栈微信

接下来,数据 Red 入栈。数据结构

从栈中取出数据时,是从最上面,也就是最新的数据开始取出的,即 Red。从栈中取出数据的操做叫做出栈this

若是再进行一次出栈操做,取出的就是 Green 了。.net

像栈这种最后添加的数据最早被取出,即后进先出的结构,咱们称为 Last In First Out,简称 LIFOcode

与链表和数组同样,栈的数据也是线性排列,但在栈中,添加和删除数据的操做只能在一端进行,访问数据也只能访问到顶端的数据,想要访问中间的数据时,就必须经过出栈操做将目标数据移到栈顶才行。blog

介绍完栈的基本知识后,接下来举一个例子,好比你们正在看公众号文章,那我就拿微信的订阅号为例。ci

如何理解栈?

首先你打开订阅号,是一个公众号列表,以后你点击了一个公众号-武培轩,进入了相应的文章列表界面,以后你点击了文章-什么是数组?,进入了文章详情页面。get

好了,如今你想返回订阅号怎么办呢?向右滑两次吧,第一次回到文章列表界面,第二次回到订阅号界面。

这时候你就发现了,这些界面的储存结构能够说是一个栈结构,你打开文章详情页面,必须通过两次入栈才能达到,你想回到订阅号界面(位于栈底),必须经历两次出栈把前面两个界面移除。

栈的实现

看到这里,相信你已经对栈有了初步的理解,栈主要包含两个操做,入栈和出栈,也就是在栈顶插入一个数据和从栈顶删除一个数据。光理解还不够,咱们还要动手去实现栈,接下来让咱们来看一看如何用代码实现一个栈。

栈有两种存储结构,即顺序存储链式存储,也就是说栈既能够用数组来实现,也能够用链表来实现。用数组实现的栈,咱们叫做顺序栈,用链表实现的栈,咱们叫做链式栈

首先来看下用数组实现的栈是怎么样的,其实现以下图所示:

顺序栈

那么我先用 Java 语言来实现下顺序栈,代码以下:

/**
 * 基于数组实现的顺序栈
 *
 * @author wupx
 * @date 2020/02/11
 */
public class ArrayStack {
    /**
     * 数组
     */
    private String[] items;
    /**
     * 栈中元素个数
     */
    private int count;
    /**
     * 栈的大小
     */
    private int n;

    /**
     * 初始化数组,申请一个大小为 n 的数组空间
     *
     * @param n
     */
    public ArrayStack(int n) {
        this.items = new String[n];
        this.n = n;
        this.count = 0;
    }

    /**
     * 入栈
     *
     * @param item
     * @return
     */
    public boolean push(String item) {
        // 数组空间不够了,直接返回 false,入栈失败。
        if (count == n) {
            return false;
        }
        // 将 item 放到下标为 count 的位置,而且 count 加一
        items[count] = item;
        ++count;
        return true;
    }

    /**
     * 出栈
     *
     * @return
     */
    public String pop() {
        // 栈为空,则直接返回 null
        if (count == 0) {
            return null;
        }
        // 返回下标为 count-1 的数组元素,而且栈中元素个数 count 减一
        String tmp = items[count - 1];
        --count;
        return tmp;
    }
}

另一种就是链式栈,它的实现以下图所示:

链式栈

再用链表去实现栈,代码以下:

/**
 * 基于链表实现的链式栈
 *
 * @author wupx
 * @date 2020/02/11
 */
public class LinkedListStack {
    private Node top = null;

    /**
     * 入栈
     *
     * @param value
     */
    public void push(int value) {
        Node newNode = new Node(value, null);
        // 判断是否栈空
        if (top != null) {
            newNode.next = top;
        }
        top = newNode;
    }

    /**
     * 出栈
     *
     * @return
     */
    public int pop() {
        if (top == null) {
            // -1 表示栈中没有数据
            return -1;
        }
        int value = top.data;
        top = top.next;
        return value;
    }

    public void printAll() {
        Node p = top;
        while (p != null) {
            System.out.print(p.data + " ");
            p = p.next;
        }
        System.out.println();
    }

    private static class Node {
        private int data;
        private Node next;

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

        public int getData() {
            return data;
        }
    }
}

在对栈有了更深一步的理解和实践后,让咱们来看下它的空间、时间复杂度各是多少呢?

无论是顺序栈仍是链式栈,咱们存储数据只须要一个大小为 n 的数组就够了。在入栈和出栈过程当中,只须要一两个临时变量存储空间,因此空间复杂度是 O(1)

入栈和出栈只会影响到最后一个元素,不涉及其余元素的总体移动,因此不管是以数组仍是以链表实现,入栈、出栈的时间复杂度都是 O(1)

总结

看完以后,相信你们都对栈有了必定的了解,让咱们总结下这篇文章的内容,栈是一种线性逻辑结构,只支持入栈和出栈操做,遵循后进先出的原则(FILO)。栈既能够经过数组实现,也能够经过链表来实现,无论基于数组仍是链表,入栈、出栈的时间复杂度都为 O(1)。

参考

《个人第一本算法书》

《算法图解》

相关文章
相关标签/搜索