栈简介 Stack

栈的本质

栈也是一种数据呈线性排列的数据结构,不过在这种结构中,我们只能访问最新添加的数据。

⨳ 栈就像是一摞书,拿到新书时我们会把它放在书堆的最上面,取书时也只能从最上面的新书开始取。

⨳ 栈也像一摞叠在一起的盘子,我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也是从上往下一个一个地依次取,不能从中间任意抽出。

⨳ 栈也像子弹夹,子弹夹只有一个口,最新压入的子弹,也是最开始射出的子弹。

总之,后进者先出(Last In First Out,简称 LIFO),就是典型的"栈"结构。

前文讲了数组和链表这两种不同的线性存储结构,而栈既可以用数组实现(顺序栈 ),也可以用链表实现(链式栈)。

栈主要 API 如下:

⨳ void push(E):入栈操作,向栈顶添加元素

⨳ E pop():出栈操作,从栈顶弹出元素

⨳ E peek():查看栈顶元素

相比数组和链表,栈就是个阉割版本,那我直接使用数组或者链表不就好了吗?为什么还要用这个"操作受限"的"栈"呢?

因为数组或链表暴露了太多的操作接口,操作上的确灵活自由,但使用时就比较不可控,比较容易出错(单一职责)。

下面就分别使用数组和链表实现一个栈。

顺序栈 ArrayStack

java 复制代码
import com.cango.array.IntArray; // 数组篇讲的代码

public class IntArrayStack {

    IntArray array ;

    public IntArrayStack(int capacity){
        array = new IntArray(capacity);
    }

    public IntArrayStack(){
        array = new IntArray();
    }


    public void push(int e){
        array.add(array.getSize(),e);
    }


    public int pop(){
        return array.remove(array.getSize()-1);
    }


    public int peek(){
        return array.get(array.getSize()-1);
    }
    
}

看完代码你可能会说,就这?对,将数组操作阉割一下就是栈。

如果 IntArray 支持动态扩容,用 IntArray 实现的栈也会支持动态扩容,多方便。

从上代码可以看出,用数组实现栈最好让数组尾部作为栈顶,因为栈只能操作栈顶的元素,对应到数组,就是只处理数组尾部的元素,不涉及数组其他元素的搬移。

链式栈 LinkedStack

将数组操作阉割一下就是顺序栈,接着将链表阉割一下实现链式栈:

java 复制代码
import com.cango.list.IntList;

public class IntLinkedStack {

    IntList list;

    public IntLinkedStack(){
        list = new IntList();
    }
    
    public void push(int e){
        list.add(0,e);
    }
    
    public int pop(){
        return list.remove(0);
    }
    
    public int peek(){
        return list.get(0);
    }

}

单向链表适合让链表头部作为栈顶,因为链表对头部节点的增删查最快。

Stack VS Deque

scala 复制代码
package java.util;

/**
 * The <code>Stack</code> class represents a last-in-first-out
 * (LIFO) stack of objects. It extends class <tt>Vector</tt> with five
 * operations that allow a vector to be treated as a stack. The usual
 * <tt>push</tt> and <tt>pop</tt> operations are provided, as well as a
 * method to <tt>peek</tt> at the top item on the stack, a method to test
 * for whether the stack is <tt>empty</tt>, and a method to <tt>search</tt>
 * the stack for an item and discover how far it is from the top.
 * <p>
 * When a stack is first created, it contains no items.
 *
 * <p>A more complete and consistent set of LIFO stack operations is
 * provided by the {@link Deque} interface and its implementations, which
 * should be used in preference to this class.  For example:
 * <pre>   {@code
 *   Deque<Integer> stack = new ArrayDeque<Integer>();}</pre>
 *
 * @author  Jonathan Payne
 * @since   JDK1.0
 */
public
class Stack<E> extends Vector<E>

这是 Java 版本的 Stack,从描述上看,官方推荐使用 Deque 代替 Stack 。

为啥呢?

Stack 类对 push、pop 方法实现很好呀,原因就是它是继承 Vector ,而不是组合 Vector (Vector 是个线程安全的数组封装),这就导致 Stack 不仅仅有 push、pop 方法,数组相关操作方法都有,Stack 存在的意义就是屏蔽多余的操作,现在 Stack 操作这么多,怎能不让开发者迷惑。

Java 官方推荐的写法是使用 Deque 接口:

java 复制代码
 Deque<Integer> stack = new ArrayDeque<Integer>();}

Deque 是双端队列的意思,也就是说可以在队列两头操作元素,那我只用一头进行插入和删除,这不就是栈了嘛。

Deque 还是暴露了多余的操作呀,是的,哪能这么办,重新构造 Stack 类吗?原来的项目怎么办?

妥协的方法就是,选择一个比较符合栈操作的类,也就是 Deque。

相关推荐
小羊没烦恼!5 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
倒头就睡的小比特5 天前
算法竞赛C++常用的STL
c++·算法
俊昭喜喜里5 天前
java中的继承和多态的区别
java
小羊没烦恼!5 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕5 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码5 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖5 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时5 天前
01-06-A-JVM排查实战详解
java·jvm
猎头南楼5 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
vipxieliang5 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot