美文网首页程序员
数据结构-栈(Stack)

数据结构-栈(Stack)

作者: 胡子先生丶 | 来源:发表于2018-11-19 21:41 被阅读0次
    一、什么是栈?

    1、后进者先出,先进者后出,这就是典型的“栈”结构。
    2、从栈的操作特性来看,是一种“操作受限”的线性表,只允许在端插入和删除数据。
    3、特定的数据结构是对特定场景的抽象,而且,数组或链表暴露了太多的操作接口,操作上的确灵活自由,但使用时就比较不可控,自然也就更容易出错。


    1.png
    二、如何实现一个“栈”?

    1、栈既可以用数组来实现,也可以用链表来实现。用数组实现的栈,叫作顺序栈,用链表实现的栈,叫作链式栈。
    2、不管基于数组还是链表,入栈、出栈的时间复杂度都为 O(1)。
    不管是顺序栈还是链式栈,存储数据只需要一个大小为 n 的数组就够了。
    在入栈和出栈过程中,只需要一两个临时变量存储空间,所以空间复杂度是 O(1)。
    入栈、出栈只涉及栈顶个别数据的操作,所以时间复杂度都是 O(1)。

    // 基于数组实现的顺序栈
    public class ArrayStack {
      private String[] items;  // 数组
      private int count;       // 栈中元素个数
      private int n;           // 栈的大小
    
      // 初始化数组,申请一个大小为 n 的数组空间
      public ArrayStack(int n) {
        this.items = new String[n];
        this.n = n;
        this.count = 0;
      }
    
      // 入栈操作
      public boolean push(String item) {
        // 数组空间不够了,直接返回 false,入栈失败。
        if (count == n) return false;
        // 将 item 放到下标为 count 的位置,并且 count 加一
        items[count] = item;
        ++count;
        return true;
      }
      
      // 出栈操作
      public String pop() {
        // 栈为空,则直接返回 null
        if (count == 0) return null;
        // 返回下标为 count-1 的数组元素,并且栈中元素个数 count 减一
        String tmp = items[count-1];
        --count;
        return tmp;
      }
    }
    
    三、支持动态扩容的顺序栈

    1、如果要实现一个支持动态扩容的栈,我们只需要底层依赖一个支持动态扩容的数组就可以了。当栈满了之后,我们就申请一个更大的数组,将原来的数据搬移到新数组中。
    2、出栈的时间复杂度是 O(1)。
    入栈操作,最好情况时间复杂度是 O(1),最坏情况时间复杂度是 O(n)。均摊时间复杂度为O(1)。

    四、栈在函数调用中的应用

    1、经典应用场景:函数调用栈。
    2、操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构, 用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈。

    五、栈在表达式求值中的应用

    1、常见的应用场景,编译器如何利用栈来实现表达式求值。比如:34+13*9+44-12/3。
    2、编译器就是通过两个栈来实现的。其中一个保存操作数的栈,另一个是保存运算符的栈。
    从左向右遍历表达式,当遇到数字,我们就直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较。
    如果比运算符栈顶元素的优先级高,就将当前运算符压入栈;如果比运算符栈顶元素的优先级低或者相同,从运算符栈中取栈顶运算符,从操作数栈的栈顶取 2 个操作数,然后进行计算,再把计算完的结果压入操作数栈,继续比较。

    相关文章

      网友评论

        本文标题:数据结构-栈(Stack)

        本文链接:https://www.haomeiwen.com/subject/utkzfqtx.html