Java-数据结构(栈)

169 阅读3分钟

栈(Stack)

栈的简介

栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作,栈的重要特点为:后进先出先进后出

image.png

例如:

image.png 栈的一个最重要的特征就是栈的插入和删除只能在栈顶进行,所以每次删除的元素都是最后进栈的元素,故栈也被称为后进先出(Last In First Out)的线性表,简称LIFO表。

Deque 的体系关系:

image.png

Java中有一个类Stack,用于表示栈,但这个类已经过时了。Java中没有单独的栈接口,栈相关方法包括在了表示双端队列的接口Deque中,主要有四个方法:

方法名描述
void push(E e);入栈,在头部添加元素,栈的空间可能是有限的,如果栈满了,会抛出异常。
E pop();出栈,返回头部元素,并且从栈中删除,如果栈为空,会抛出异常。
E peek();查看栈头部元素,不修改栈,如果栈为空,返回null。
boolean isEmpty();判断栈是否为空,如果为空则返回true;否则返回false。

【示例】使用LinkedList实现栈操作

/*
    @Author xiangge
    @Date 2023/7/19 
    @Description 栈结构
        void push(E e);入栈,在头部添加元素,栈的空间可能是有限的,如果栈满了,会抛出异常。
        E pop();出栈,返回头部元素,并且从栈中删除,如果栈为空,会抛出异常。
        E peek();查看栈头部元素,不修改栈,如果栈为空,返回null。
        boolean isEmpty();判断栈是否为空,如果为空则返回true;否则返回false。
*/
public class DequeDemo1 {
    public static void main(String[] args) {
        Deque<Integer> deque = new LinkedList<>();
        deque.push(11);
        deque.push(22);
        deque.push(33);
        deque.push(44);
        deque.push(55);
        System.out.println(deque);//[55, 44, 33, 22, 11]
        while (!deque.isEmpty()){
            System.out.println(deque.pop());
        }
        System.out.println(deque);
    }
}

栈的实现方式

每个栈都有一个栈顶指针,它初始值为-1,且总是指向最后一个入栈的元素,栈有两种处理方式,即进栈(push)和出栈(pop),因为在进栈只需要移动一个变量存储空间,所以它的时间复杂度为O(1),但是对于出栈分两种情况,栈未满时,时间复杂度也为O(1),但是当栈满时,需要重新分配内存,并移动栈内所有数据,所以此时的时间复杂度为O(n)。以下举例栈结构的两种实现方式,顺序表存储和链表存储。

数组模拟栈实现

要求:需要限制栈的最大容量。
package com.huayu.stack;
/**
 * 数组模拟栈(限制最大容量)
 */
public class ArrayStack<T> {
    /**
     * 存放数据的容器
     */
    private Object[] elementData;
    /**
     * 指向栈顶的指针
     */
    private int top = -1; // 默认值为-1
    /**
     * 保存栈的最大容量
     */
    private int maxSize;
    /**
     * 构造方法
     * @param maxSize 设置栈的最大容量
     */
    public ArrayStack(int maxSize) {
        this.elementData = new Object[maxSize];
        this.maxSize = maxSize;
    }
    
    /**
     * 入栈操作
     * @param value
     */
    public void push(T element) {
        // 1.判断栈是否已满
        if(isFull()) {
            throw new RuntimeException("栈已满,无法执行入栈操作");
        }
        // 2.添加数据入栈
        elementData[++top] = element;
    }
    /**
     * 出栈操作
     */
    public T pop() {
        // 1.判断栈是否为空
        if(isEmpty()) {
            throw new RuntimeException("栈为空,无执行出栈操作");
        }
        // 2.获取需要出栈的元素
        T value = (T)elementData[top];
        // 3.清空栈顶存放的数据
        elementData[top] = null;
        // 4.修改栈顶指针的指向
        top--;
        // 6.返回需要取出的栈顶元素
        return value;
    }
    /**
     * 检查栈是否为空
     * @return
     */
    public boolean isEmpty() {
        return top == -1;
    }
    
    /**
     * 判断栈是否已满
     * @return
     */
    private boolean isFull() {
        return maxSize - 1 == top;
    }
}

链表模拟栈实现

要求:无需限制链表的最大容量。
package com.huayu.p3.demo;
/**
 * 链表模拟栈(不限制最大容量)
 */
public class LinkedListStack<T> {
    /**
     * 保存单链表首节点
     */
    private StackNode<T> topNode;
    /**
     * 保存实际添加元素的个数
     */
    private int size;
    /**
     * 入栈操作(链表的头部开始插入)
     * @param element 需要入栈的数据
     */
    public void push(T element) {
        // 1.把element元素封装成节点对象
        StackNode<E> node = new StackNode<>(element);
        // 2.把node节点添加进入链表(把node插入到topNode的前面)
        // 把node节点的next指向topNode节点
        node.next = topNode;
        // 更新topNode指向node节点
        topNode = node;
        // 3.更新size的值,也就是执行size++操作
        size++;
    }
​
    /**
     * 出栈操作(删除单链表首节点)
     * @return 返回栈顶的数据
     */
    public T pop() {
        // 判断栈是否为空
        if(isEmpty()) {
            throw new RuntimeException("栈为空,无执行出栈操作");
        }
        // 定义tempNode变量,来零时保存headNode节点
        StackNode<T> tempNode = topNode;
        // 更新topNode的值,将topNode.next 改为:topNode
        topNode = topNode.next;
        // 完成出栈操作后,size递减
        size--;
        // 返回栈顶的数据
        return tempNode.data;
    }
​
    /**
     * 获取栈顶元素(不会删除栈顶元素)
     * @return
     */
    public T peek() {
        // 判断栈是否为空
        if (isEmpty()) {
            return null;
        }
        // 返回栈顶数据
        return topNode.data;
    }
​
    /**
     * 判断栈是否为空
     * @return 如果栈为空,则返回true;如果栈非空,则返回false。
     */
    public boolean isEmpty() {
        return size == 0;
    }
​
    /**
     * 栈节点类
     */
    private static class StackNode<T> {
        /**
         * 节点中存放的数据
         */
        private T data;
        /**
         * 指向下一个节点
         */
        private StackNode<T> next;
        /**
         * 构造方法
         * @param data
         */
        public StackNode(T data, StackNode<T> next) {
            this.data = data;
            this.next = next;
        }
    }
}