栈结构

142 阅读4分钟

栈(Stack)又名堆栈,它是一种运算受限的 线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为 栈顶,相对地,把另一端称为 栈底。向一个栈插入新元素又称作进栈入栈压栈 ,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作 出栈退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

栈是一种具有 「先入后出」 特点的抽象数据结构,可使用数组或链表实现。使用数组实现的栈也成为 顺序栈,使用链表实现的栈成为 链栈

「先入后出」 可以用手枪弹夹比喻,弹夹中最先放入的子弹最后才会打出去,而最后放入的子弹第一枪就打出去了。

压栈 方法名一般叫做 push出栈 方法名一般叫做 pop。 我们先来画图看看入栈和出栈的过程

stack.push(1); // 元素 1 入栈 
stack.push(2); // 元素 2 入栈 
stack.pop(); // 出栈 -> 元素 2 
stack.pop(); // 出栈 -> 元素 1

image.png

没什么问题,这看起来非常好理解。所谓先入后出就像你上班坐公交车一样,先上车的乘客司机总会让你往里走走,因为里面 “还有位置”,但是下车时总是后上车的乘客先下车,因为离车门近,反而先上车的乘客因为在里面的“位置上坐着”,离车门远,总是后下车。

顺序栈和链栈的优缺点

上面有提到过,栈可使用数组或链表实现。使用数组实现的栈也称为 循序栈,使用链表实现的栈称为 链栈

但是这两种栈的优缺点是什么,我们应该如何选择呢?首先我们需要知道数组和链表的优缺点 数组的优缺点:

优点: 数组拥有非常高效的随机访问能力,只要给出下标,就可以用常量时间找到对应元素。

缺点: 数组的劣势体现在插入和删除元素方面。由于数组元素连续紧密地存储在内存中,插入、删除元素都会导致大量元素被迫移动或者重新开辟内存扩容,影响效率。

总结: 数组所适合的是读操作多、写操作少的场景!

链表的优缺点:

优点:插入和删除速度快,保留原有的逻辑顺序,在插入或者删除一个元素的时候,只需要改变指针指向即可。没有空间限制,存储元素无上限,只与内存空间大小有关.

缺点:查找速度比较慢,因为在查找时,需要循环遍历链表。

总结:链表适合的是读操作少、写操作多的场景!

栈的特点:

  • 元素数量不固定
  • 对线性表尾部有频繁的插入和删除操作(push和pop)

无论从那个方面看,使用链表来实现栈都是一个更好的选择,但是如果我们能避开数组的缺点来使用栈的话,数组的顺序存储也带来不少好处。

顺序栈

顺序栈使用数组来实现,将数组的第一项作为栈低,然后维护一个指针来表示栈顶。由于数组的缺点我们应该尽量使用固定的栈大小,也就是限制栈用元素的数量。下图是一个大小为5的栈

image.png 当栈顶指针为 -1 时表示栈中没有元素,即 空栈

我们来看一下简单的模拟实现

 class Stsck {
            constructor() {
                this.items = []//栈的数据存储在数组中
            }
            push(item) {
                this.items.push(item)//入栈操作
            }
            pop() {
                if (this.isEmpty()) {
                    return null //栈为空时返回null
                }
                return this.items.pop()//出栈操作 从数组末尾删除并返回元素
            }
            peek() {
                if (this.isEmpty()) {
                    return null //栈为空时返回null
                }
                return this.items[this.items.length - 1]  //返回栈顶元素但不删除
            }
            isEmpty() {
                return this.items.length === 0//判断栈是否为空
            }
            clear() {
                this.items = []//清空栈
            }
            size() {
                return this.items.length//返回栈的长度
            }
        }

        // 使用Stack
        const stsck = new Stsck()
        stsck.push(1)
        stsck.push(2)
        stsck.push(3)
        console.log(stsck.peek());//返回最顶端的元素
        console.log(stsck.pop());//删除顶端的元素
        console.log(stsck.size());//栈的长度
        stsck.clear()//清空栈
        console.log(stsck.isEmpty());//为空返回true