数据结构-栈

  • 定义

    • 栈是一种特殊的线性数据结构,它的特点是后进先出(LIFO,Last In First Out)。
  • 顺序栈

    public class SeqStack {
        private int[] stack;
        private int top;
    
        public SeqStack(int size) {
            stack = new int[size];
            top=-1;
        }
        //1.判断栈是否为空
        public boolean isEmpty() {
            return top==-1;
        }
        //2.判断栈是否满
        public boolean isFull() {
            return top==stack.length-1;
        }
        //3.入栈
        public void push(int value){
            if (top==stack.length-1){
                throw new RuntimeException("栈已满");
            }
            //先执行top+1在作为索引值
            stack[++top]=value;
        }
        //4.出栈
        public int pop(){
            if (top==-1){
                throw new RuntimeException("栈为空");
            }
            //先作为索引值,再执行top-1
            return stack[top--];
        }
        //5.返回栈顶
        public int peek(){
            if (top==-1){
                throw new RuntimeException("栈为空");
            }
            return stack[top];
        }
    }
    
  • 链栈

    //节点
    public class Node {
        int data;
        Node next;
    
        public Node(int data) {
            this.data = data;
        }
    }
    
    public class LinkedStack {
        private Node top;
    
        public LinkedStack() {
            top = null;
        }
        //1.判断栈是否为空
        public boolean isEmpty() {
            return top == null;
        }
        //2.入栈(链表头插法)
        public void push(int value){
            Node newNode = new Node(value);
            newNode.next = top;
            top = newNode;
        }
        //3.出栈
        public int pop(){
            if (isEmpty()){
                throw new RuntimeException("Stack is empty");
            }else {
                int data = top.data;
                top=top.next;
                return data;
            }
        }
        //4.返回栈顶元素
        public int peek(){
            if (isEmpty()){
                throw new RuntimeException("Stack is empty");
            }else {
                return top.data;
            }
        }
    }
    
  • Java中实现顺序栈的类

    • Stack:它是Vector类的子类,Vector类实现了可增长的对象数组。Vector的大小可以根据需要增大或缩小,以便在创建Vector后添加和删除项目。

      与新的集合实现不同, Vector是同步的。 如果不需要线程安全实现,建议使用ArrayList代替Vector 。Vector是一个同步的容器,所有的方法都实现了同步,这意味着它可以在多线程环境下安全地访问。但是,同步会引入额外的开销,导致性能下降。因此,在单线程环境下,通常不推荐使用Vector。

      public class StackDemo {
          private Stack<Integer> stack;
      
          public StackDemo() {
              this.stack=new Stack<>();
          }
          // 判断是否为空
          public boolean isEmpty(){
              return stack.isEmpty();
          }
          // 获取栈的大小
          public int getSize(){
              return stack.size();
          }
          //入栈
          public void push(int value){
              stack.push(value);
          }
          //出栈
          public int pop(){
              return stack.pop();
          }
          //获取栈顶元素
          public int peek(){
              return stack.peek();
          }
      }
      
      • Stack逐渐被淘汰的原因

        Vector和SynchronousQueue都可以用于实现同步栈,但它们在使用上有所不同。

        Vector是同步的,它的所有方法都是同步的,因此在多线程环境下可以安全地使用。但是,Vector的性能相对较低,因为它所有的方法都使用了同步机制。

        SynchronousQueue是一个阻塞队列,它没有缓冲区,producer和consumer操作必须相互等待。这种特性使得它非常适合用于线程之间的消息传递,也可以用作同步栈。但是,在使用SynchronousQueue时需要手动实现同步,例如使用put和take方法。

        在多线程环境下,如果需要使用同步的容器,可以选择使用Vector;如果需要手动实现同步,可以选择使用SynchronousQueue。

    • ArrayList:在实现栈的数据结构时,ArrayDeque 的效率更高。由于 ArrayDeque 是双端队列,可以在队列的头部和尾部进行元素的插入和删除操作,这使得它非常适合用来实现栈。相比之下,ArrayList 要实现栈需要在尾部进行插入和删除操作,而在头部进行这些操作的效率较低。ArrayList 在头部进行插入和删除操作的效率较低是因为这些操作需要移动数组中的元素。当在 ArrayList 的头部插入或删除元素时,需要将数组中的所有元素向后移动一个位置,这样的时间复杂度是 O(n),其中 n 是数组中的元素个数。相比之下,ArrayDeque 是通过循环数组实现的,可以在头部和尾部进行高效的插入和删除操作,时间复杂度是 O(1)。

    • ArrayDeque

      public class ArrayDequeStackDemo {
          private ArrayDeque<Integer> stack;
      
          public ArrayDequeStackDemo() {
              stack = new ArrayDeque<>();
          }
          //入栈
          public void push(int item) {
              stack.push(item);
          }
          //出栈
          public int pop() {
              return stack.pop();
          }
          //返回栈顶
      
          public Integer peek() {
              return stack.peek();
          }
          //返回栈的大小
      
          public int size() {
              return stack.size();
          }
          //判断栈空
      
          public boolean isEmpty() {
              return stack.isEmpty();
          }
      }
      
  • Java中的LinkedLIst类实现链栈

    public class LinkedListStack {
        private LinkedList<Integer> stack;
    
        public LinkedListStack() {
            stack = new LinkedList<>();
        }
        //判空
        public boolean isEmpty() {
            return stack.isEmpty();
        }
        //入栈
        public void push(int value) {
            stack.push(value);
        }
        //出栈
        public int pop() {
            return stack.pop();
        }
        //返回栈顶元素
        public int peek() {
            return stack.peek();
        }
        //返回栈的大小
        public int size() {
            return stack.size();
        }
    }
    
  • Java中的ArrayDeque类实现链栈

    public class ArrayDequeStack {
        private ArrayDeque<Integer> stack;
        public ArrayDequeStack() {
            stack = new ArrayDeque<>();
        }
        //判栈空
        public boolean isEmpty() {
            return stack.isEmpty();
        }
        //入栈
        public void push(int value) {
            stack.push(value);
        }
        //出栈
        public int pop() {
            return stack.pop();
        }
        //返回栈顶
        public int peek() {
            return stack.peek();
        }
    }
    
  • LinkedLIst和ArrayDeque实现链栈的区别

    • 数据结构:ArrayDeque 使用数组实现,而 LinkedList 使用链表实现。数组在内存中是连续存储的,而链表的节点在内存中可以是离散的。这使得 ArrayDeque 的访问速度通常比 LinkedList 快,但 ArrayDeque 的最大容量是有限的,而 LinkedList 的大小理论上可以无限扩展。
    • 方法:ArrayDeque 和 LinkedList 都提供了添加元素、删除元素、获取元素等基本方法。但是,ArrayDeque 提供了更多的方法,例如 peekFirst() 和 peekLast(),它们可以分别获取队列的第一个和最后一个元素,而 LinkedList 没有提供这些方法。
    • 性能:ArrayDeque 在添加或删除元素时通常比 LinkedList 更快,因为它不需要进行额外的指针操作。但是,ArrayDeque 的最大容量是有限的,当达到最大容量时,它会自动扩容,这可能会导致性能下降
    • 疑问:ArrayDeque为什么能实现链栈,它不是用数组实现的吗?
      ArrayDeque 中的元素被存储在一个循环数组中,这个数组的首尾相接,形成一个环形链表。这样,ArrayDeque 可以在队列的一端添加元素,在另一端删除元素,而不需要移动其他元素。这种特性使得 ArrayDeque 可以在链栈中作为双向队列使用。
  • 顺序栈和链栈的优缺点以及使用场景

    • 顺序栈
      • 优点

        • 支持随机访问:顺序栈支持通过索引随机访问栈中的元素。
        • 内存占用低:顺序栈使用一块连续的内存空间,内存占用相对较低。
      • 缺点

        • 长度固定:顺序栈的长度固定,当栈满时,无法再添加元素,需要动态扩容。
        • 慢入栈和出栈:顺序栈的入栈(push)和出栈(pop)操作的时间复杂度为 O(1),但需要移动其他元素。
      • 使用场景

        需要随机访问栈中元素的场景,如表达式求值、括号匹配等。

    • 链栈
      • 优点

        • 长度可变:链栈的长度可以动态变化,不会像顺序栈那样需要动态扩容。
        • 快入栈和出栈:链栈的入栈(push)和出栈(pop)操作的时间复杂度为 O(1)。
      • 缺点

        • 不支持随机访问:链栈不支持通过索引随机访问栈中的元素。
        • 内存占用高:链栈的每个节点需要存储数据和指向下一个节点的引用,内存占用相对较高。
      • 使用场景

        需要动态调整栈长度的场景,如括号匹配、递归等。

相关推荐
weixin_462428477 分钟前
使用 Caffeine 缓存并在业务方法上通过注解实现每3到5秒更新缓存
java·缓存
程序媛小果9 分钟前
基于java+SpringBoot+Vue的桂林旅游景点导游平台设计与实现
java·vue.js·spring boot
骑鱼过海的猫12311 分钟前
【java】java通过s3访问ceph报错
java·ceph·iphone
杨充17 分钟前
13.观察者模式设计思想
java·redis·观察者模式
Lizhihao_19 分钟前
JAVA-队列
java·开发语言
算法歌者22 分钟前
[算法]入门1.矩阵转置
算法
喵叔哟28 分钟前
重构代码之移动字段
java·数据库·重构
喵叔哟28 分钟前
重构代码之取消临时字段
java·前端·重构
fa_lsyk31 分钟前
maven环境搭建
java·maven
林开落L36 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode