数据结构奇妙旅程之栈和队列

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱

ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ---CSDN博客

本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶
个人主页xiaoxieʕ̯•͡˔•̯᷅ʔ---CSDN博客

系列专栏:xiaoxie的JAVA系列专栏------CSDN博客●'ᴗ'σσணღ*
我的目标:"团团等我💪( ◡̀_◡́ ҂)"

( ⸝⸝⸝›ᴥ‹⸝⸝⸝ )欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​+关注(互三必回)!

一.栈(Stack)

1.概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈
顶,另一端称为栈底。栈中的数据元素遵守后进先出 LIFO ( Last In First Out )的原则。
压栈:栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶
出栈:栈的删除操作叫做出栈。 出数据在栈顶

2.栈的模拟实现

可以看出栈Stack 是继承与Vector的, Vector和ArrayList类似,我们可以得到以下的栈的模拟实现,帮助我们更好的理解栈的使用。

java 复制代码
//这是栈内存储Integer的模拟,当然栈是泛型,这里只是Integer的模拟
class MyStack {
    public int[] arr;
    public int size;
    public MyStack() {
     arr = new int[10];
    }
    //入栈
    public int push(int e) {
        ensureCapacity();
        arr[size++] = e;
        return e;
    }
    //判断栈是否满
    private void ensureCapacity() {
        if (size == arr.length) {
            arr = Arrays.copyOf(arr, size * 2);
        }
    }
    //栈顶元素
    public int peek() {
        if(empty()) {
            System.out.println("栈为空,无元素");
            return -1;
        }
        return arr[size-1];
    }
    //出栈
    public int pop() {
        int tmp = peek();
        size--;
        return tmp;
    }
    //判断栈是否为空
    public boolean empty() {
        return this.size == 0;
    }

}

3.栈、虚拟机栈、栈帧有什么区别呢

栈、虚拟机栈和栈帧是计算机科学中的概念,它们之间有以下区别:

  1. 栈:栈是一种具有后进先出(Last-In-First-Out,LIFO)特性的数据结构,可以存储和检索数据。在计算机中,栈通常用于管理函数调用和局部变量的分配。栈在内存中是连续存储的一块区域,主要用于存储函数调用的上下文信息以及局部变量。

  2. 虚拟机栈:虚拟机栈是Java虚拟机(JVM)为每个线程分配的内存区域,用于存储方法的调用和执行信息。每个线程在执行方法时,都会在虚拟机栈中创建一个栈帧,栈帧中包含了方法的局部变量、操作数栈、方法返回地址等信息。

  3. 栈帧:栈帧是方法在虚拟机栈中的表示,用于存储方法的局部变量、操作数栈、方法返回地址等信息。每个方法在执行时,都会在虚拟机栈中创建一个对应的栈帧,方法的参数、局部变量以及中间计算结果都存储在栈帧中。当方法执行完毕后,对应的栈帧就会被销毁。

总结来说,栈是一种数据结构,用于存储和检索数据;虚拟机栈是Java虚拟机为每个线程分配的内存区域,用于存储方法的调用和执行信息;栈帧是方法在虚拟机栈中的表示,用于存储方法的局部变量、操作数栈、方法返回地址等信息。

4.栈的应用

155.最小栈

题目分析:

我们都知道栈是一个先进后出的数据结构,所以我们只使用一个普通栈是无法实现最小栈,应该要用两个栈来模拟实现最小栈。

如果两个栈都为空就先将元素入栈

然后下一个元素先入stack 栈 之后和minstack 比较如果 < minstack的栈顶元素,就入 minstack 否则就不入,

这样就通过用两个普通栈模拟最小栈了

java 复制代码
class MinStack {
     Stack<Integer> stack;
     Stack<Integer> minstack;
    public MinStack() {
        stack = new Stack<>();
        minstack = new Stack<>();
    }
    public void push(int val) {
        stack.push(val);
        if(minstack.empty()) {
            minstack.push(val);
        }else {
            if(minstack.peek() >= val) {
                minstack.push(val);
            }
        }
    }
    
    public void pop() {
        int tmp = stack.pop();
        if(tmp == minstack.peek()) {
            minstack.pop();
        }
    }
    
    public int top() {
      return stack.peek();
    }
    
    public int getMin() {
       return minstack.peek();
    }
}

1.面试进阶

是否可以不用辅助栈呢

栈中每个元素代表的是要压入元素与当前栈中最小值的差值 有个很重要问题: 在弹出时如何维护min? 因为每次压入新的元素时,压入的都是与当前栈中最小值的差值(还未压入当前元素),故在弹出元素时,若弹出了当前最小值,因为栈中记录了当前元素与【之前】最小值的差值,故根据这个记录可以更新弹出元素后的最小值。 接下来看代码吧

java 复制代码
class MinStack {
    // 记录每个元素与【未压入】该元素时栈中最小元素的差值
    LinkedList<Long> stack;
    // 当前【已压入】栈中元素的最小值
    private long min;
    public MinStack() {
        stack = new LinkedList();
    }
    
    public void push(int val) {
        // 压入第一个元素
        if(stack.isEmpty()){
            min = val;
            stack.addFirst(0L);
            return;
        }
        // 栈不为空时,每次压入计算与min的差值后压入结果
        stack.push((long)val-min);
        // 更新min
        min = Math.min((long)val,min);
        // 上面两个语句是不能颠倒的!一定是先压入,在更新,因为min一定是当前栈中的最小值
    }
    
    public void pop() {
        long pop = stack.removeFirst();
        // 当弹出元素小于0时,说明弹出元素是当前栈中的最小值,要更新最小值
        if(pop<0){
            // 因为对于当前弹出的元素而言,计算压入栈中的值时,计算的是该元素与【未压入】该元素时
            // 栈中元素的最小值的差值,故弹出该元素后栈中的最小值就是未压入该元素时的最小值
            // 即当前元素的值(min)减去两者的差值
            long lastMin = min;
            min = lastMin - pop;
        }
        // 若大于等于0,不会对min有影响
    }
    
    public int top() {
        long peek = stack.peek();
        // 若当前栈顶小于等于0,说明最小值就是栈顶元素
        if(peek<=0) return (int)min;
        // 否则就是min+peek
        return (int)(min+peek);
    }
    
    public int getMin() {
        return (int)min;
    }
}

二.队列

1.概念

队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First
In First Out) 入队列:进行插入操作的一端称为 队尾( Tail/Rear 出队列:进行删除操作的一端称为 队头 Head/Front

2.队列的模拟实现

java 复制代码
public class Queue {

    private int maxSize;
    private int[] queueArray;
    private int front;
    private int rear;
    private int nItems;

    public Queue(int size) {
        maxSize = size;
        queueArray = new int[maxSize];
        front = 0;
        rear = -1;
        nItems = 0;
    }

    public void insert(int value) {
        if (rear == maxSize - 1) {
            rear = -1;
        }
        queueArray[++rear] = value;
        nItems++;
    }

    public int remove() {
        int temp = queueArray[front++];
        if (front == maxSize) {
            front = 0;
        }
        nItems--;
        return temp;
    }

    public int peek() {
        return queueArray[front];
    }

    public boolean isEmpty() {
        return (nItems == 0);
    }

    public boolean isFull() {
        return (nItems == maxSize);
    }

    public int size() {
        return nItems;
    }

}
 

三.面试题

用队列实现栈

大家都清楚栈是先进后出,队列是先进先出的数据结构,如果要用队列模拟实现栈,仅靠一个队列是无法实现,需要用到两个队列,来模拟模拟实现栈

如果两个队列都为空 就入q1 的队

谁不为空就入队那个队列,如果要出栈的话,就把除了要出栈的那个元素之外的元素,入到另一个 队列中。

代码如下

java 复制代码
class MyStack {
   Queue<Integer> q1;
   Queue<Integer> q2;
    public MyStack() {
        q1 = new LinkedList<>();
        q2 = new LinkedList<>();
    }

    public void push(int x) {
        if(empty()) {
            q1.add(x);
            return;
        }if( ! q1.isEmpty()) {
            q1.add(x);
        }else {
            q2.add(x);
        }
    }

    public int pop() {
        if(empty()) return -1;
        if(!q1.isEmpty()) {
            int size1 = q1.size();
            for (int i = 0;i < size1-1;i++) {
                q2.add(q1.poll());
            }
            return q1.poll();
        }else {
            int size2 = q2.size();
            for (int i = 0; i < size2-1; i++) {
                q1.add(q2.poll());
            }
            return q2.poll();
        }
    }

    public int top() {
        if(empty()) {
            return -1;
        }int temp = -1;
        if(!q1.isEmpty()) {
            int size2 = q1.size();
            for(int i = 0; i < size2;i++) {
                temp = q1.poll();
                q2.offer(temp);
            }
            return temp;
        }else {
                 int size2 = q2.size();
                for(int i = 0;i < size2; i++) {
                   temp = q2.poll();
                   q1.offer(temp);
                }
            return temp;
        }
    }
    public boolean empty() {
        return q1.isEmpty() && q2.isEmpty();
    }
}

用栈实现队列

栈实现队列的出队操作效率低下:栈底元素(对应队首元素)无法直接删除,需要将上方所有元素出栈。

两个栈可实现将列表倒序:设有含三个元素的栈 A = [1,2,3] 和空栈 B = [] 。若循环执行 A 元素出栈并添加入栈 B ,直到栈 A 为空,则 A = [] , B = [3,2,1] ,即栈 B 元素为栈 A 元素倒序。

利用栈 B 删除队首元素:倒序后,B 执行出栈则相当于删除了 A 的栈底元素,即对应队首元素。

因此,可以设计栈 A 用于加入队尾操作,栈 B 用于将元素倒序,从而实现删除队首元素。

代码如下

java 复制代码
class MyQueue {
    Stack<Integer> s1;
    Stack<Integer> s2;
    public MyQueue() {
        s1 = new Stack<>();
        s2 = new Stack<>();
    }
    public void push(int x) {
        s1.push(x);
    }
    public int pop() {
        if(s2.empty()) {
            while(!s1.empty()) {
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
    public int peek() {
        if(s2.empty()){
            while(!s1.empty()){
                s2.push(s1.pop());
            }
        }
        return s2.peek();
    }
    public boolean empty() {
        return s1.empty() && s2.empty();
    }
}

以上就是栈和队列的所有内容了,在这里博主还是要说一句,要想理解栈和队列还是要多多刷类似的题目,一定可以更好的理解他们的使用

相关推荐
考虑考虑39 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干1 小时前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying1 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
Wo3Shi4七3 小时前
哈希冲突
数据结构·算法·go
Zz_waiting.3 小时前
Javaweb - 10.4 ServletConfig 和 ServletContext
java·开发语言·前端·servlet·servletconfig·servletcontext·域对象
全栈凯哥3 小时前
02.SpringBoot常用Utils工具类详解
java·spring boot·后端
兮动人3 小时前
获取终端外网IP地址
java·网络·网络协议·tcp/ip·获取终端外网ip地址