【LeetCode刷题日记】225.用队列实现栈--三招实现栈操作(多种思维)

🔥个人主页:北极的代码(欢迎来访)

🎬作者简介:java后端学习者

❄️个人专栏:苍穹外卖日记SSM框架深入JavaWeb

命运的结局尽可永在,不屈的挑战却不可须臾或缺!

摘要:

本文探讨了用队列实现栈操作的三种方法。

重点分析了双队列实现方案,通过que2作为辅助队列,在push操作时将que1元素转移到que2后交换队列,确保新元素始终位于队首(栈顶),实现先进后出特性。该方法push操作需O(n)时间,pop/top操作仅需O(1)时间。

文章还对比了单向队列与双端队列的实现差异,指出双端队列可通过旋转操作更高效地实现栈功能。最后给出了Java实现的三种代码示例,包括双队列、单队列和双端队列方案,并比较了不同队列接口的操作特点及异常处理机制。

题目背景:用队列实现栈(可直达)

使用队列实现栈的下列操作:

  • push(x) -- 元素 x 入栈
  • pop() -- 移除栈顶元素
  • top() -- 获取栈顶元素
  • empty() -- 返回栈是否为空

注意:

  • 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
  • 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
  • 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

题目解析:

单向队列实现

有的同学可能疑惑这种题目有什么实际工程意义,其实很多算法题目主要是对知识点的考察和教学意义远大于其工程实践的意义,所以面试题也是这样

刚刚做过栈与队列:我用栈来实现队列怎么样? (opens new window)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下不行

队列模拟栈,其实一个队列就够了,那么我们先说一说两个队列来实现栈的思路。

队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序。

所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。

但是依然还是要用两个队列来模拟栈,只不过没有输入和输出的关系,而是另一个队列完全用来备份的!

如下面动画所示,用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。

如图所示,我们利用了两个队列来实现栈的操作

一个队列是与栈中保持相同元素的队列,另一个是辅助队列。

我们先把元素放到辅助队列的队尾中,然后下面进行判断,如果是第一个队列不是空的,就把第一个队列的头部取出放到第二个队列的尾部,这是个while循环,不是仅仅处理一个元素,可以说是把第一个队列的所有元素都放到第二个队列的尾部,依次添加。

之后进行交换,把队列2的元素转移到队列1, 然后队列2 为空,经过这种操作,对于新添加的元素,就自然而然地放在了队列1的队首了,也就实现类栈的先进后出,后进先出的逻辑了。

流程解析

假设当前 queue1 = [栈底 ... 栈顶] = [a, b, c](队首 a,队尾 c)。

  1. queue2 先放 x 进队尾 → queue2 = [x](队首 x,队尾 x)。

  2. queue1 全部出队并入队到 queue2

    • 出队 a,入 queue2 → [x, a]

    • 出队 b,入 queue2 → [x, a, b]

    • 出队 c,入 queue2 → [x, a, b, c]

  3. 交换后:

    • queue1 = [x, a, b, c](队首 x 是栈顶,队尾 c 是栈底)

    • queue2 变空。

这样每次 push 后,queue1队首就是最新元素 (栈顶),poll() 就是栈的 pop()

  • pop()queue1.poll() ------ 队首出队,即栈顶元素。

  • top()queue1.peek() ------ 查看队首元素但不删除。

  • empty()queue1.isEmpty() ------ 队列为空则栈为空。

  1. 初始 queue1 = []

    push(1): queue2=[1], queue1 空 → 交换 → queue1=[1], queue2=[]

  2. push(2): queue2=[2], 移 queue1 到 queue2 → queue2=[2,1], 交换 → queue1=[2,1](队首 2 是栈顶)

  3. push(3): queue2=[3], 移 queue1 → queue2=[3,2,1], 交换 → queue1=[3,2,1]

  4. pop(): 队首 3 出队 → queue1=[2,1], 返回 3

  5. top(): peek → 2

输出符合栈顺序。


  • push:O(n),因为要把 queue1 中 n 个元素搬到 queue2。

  • pop / top:O(1)。

  • empty:O(1)。


✅ 优点:只用标准队列操作(offer/poll/peek),逻辑清晰。

❌ 缺点:push 消耗 O(n),不是最优实现(可以用一个队列做到 O(1) push 和 O(n) pop)。

双端队列实现:

主要思想是就是把队尾的元素旋转到队尾,

复制代码
que1.addLast(que1.peekFirst());  // 1. 把队首元素复制一份,添加到队尾
que1.pollFirst();                 // 2. 删除原来的队首元素

具体执行过程(队列 [1, 2, 3])

操作 队列状态 说明
初始 [1, 2, 3] 栈底→栈顶
size=3, size--=2
第1次循环 [2, 3, 1] 把 1 移到队尾
第2次循环 [3, 1, 2] 把 2 移到队尾
循环结束 [3, 1, 2] 队首 3 是栈顶
pollFirst() [1, 2] 返回 3 ✅

pollFirst() 的特点

方法 作用 队列空时
pollFirst() 删除并返回队首 返回 null
removeFirst() 删除并返回队首 抛出异常
peekFirst() 只看不删 返回 null
getFirst() 只看不删 抛出异常
对比:
java 复制代码
java

Queue<Integer> queue1 = new LinkedList<>();
Queue<Integer> queue2 = new LinkedList<>();
  • Queue 接口 :只支持一端进、另一端出的单向操作

    • 从队尾添加:offer() / add()

    • 从队首删除:poll() / remove()

    • 查看队首:peek() / element()

  • 没有直接操作队尾的方法 (如 peekLast()pollLast()

java 复制代码
java

Deque<Integer> que1 = new ArrayDeque<>();
  • Deque 接口 :支持双端操作(Double-Ended Queue)

    • 可以在队首/队尾两端进行添加、删除、查看

    • 方法如:addFirst()、addLast()、pollFirst()、pollLast()、peekLast() 等

特性 Queue(单向队列) Deque(双端队列)
操作方向 仅一端进、一端出 两端都可进可出
查看队尾 ❌ 不支持 peekLast()
从队尾删除 ❌ 不支持 pollLast()
从队首添加 ❌ 不支持 addFirst()
栈实现方式 需要两个队列配合 一个 Deque 就够了
补充说明:

但是需要注意的是**,单向队列我们也可以仅仅通过一个队列来实现栈**,主要的逻辑就是通过循环把队列的队首依次拿出来放到队尾,这样我们新添加的元素就到了队首,也就是实现了栈。

题目答案

单向队列(两个Queue)
java 复制代码
class MyStack {

    Queue<Integer> queue1; // 和栈中保持一样元素的队列
    Queue<Integer> queue2; // 辅助队列

    /** Initialize your data structure here. */
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        queue2.offer(x); // 先放在辅助队列中
        while (!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }
        Queue<Integer> queueTemp;
        queueTemp = queue1;
        queue1 = queue2;
        queue2 = queueTemp; // 最后交换queue1和queue2,将元素都放到queue1中
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        return queue1.poll(); // 因为queue1中的元素和栈中的保持一致,所以这个和下面两个的操作只看queue1即可
    }
    
    /** Get the top element. */
    public int top() {
        return queue1.peek();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.isEmpty();
    }
}
双向队列(一个Deque)
java 复制代码
class MyStack {
    // Deque 接口继承了 Queue 接口
    // 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
    Deque<Integer> que1;
    /** Initialize your data structure here. */
    public MyStack() {
        que1 = new ArrayDeque<>();
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        que1.addLast(x);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        int size = que1.size();
        size--;
        // 将 que1 导入 que2 ,但留下最后一个值
        while (size-- > 0) {
            que1.addLast(que1.peekFirst());
            que1.pollFirst();
        }

        int res = que1.pollFirst();
        return res;
    }
    
    /** Get the top element. */
    public int top() {
        return que1.peekLast();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return que1.isEmpty();
    }
}
单向队列(一个Queue)
java 复制代码
class MyStack {

    Queue<Integer> queue;

    public MyStack() {
        queue = new LinkedList<>();
    }

    //每 offer 一个数(A)进来,都重新排列,把这个数(A)放到队列的队首
    public void push(int x) {
        queue.offer(x);
        int size = queue.size();
        //移动除了 A 的其它数
        while (size-- > 1)
            queue.offer(queue.poll());
    }

    public int pop() {
        return queue.poll();
    }

    public int top() {
        return queue.peek();
    }

    public boolean empty() {
        return queue.isEmpty();
    }
}
相关推荐
Mr_pyx1 小时前
【告别for循环】Java Stream 流式编程精通:从入门到源码级的性能优化
java·开发语言·性能优化
:1211 小时前
java基础--数组
java·开发语言
爱上好庆祝2 小时前
学习js第一天(出发新世界)
开发语言·前端·javascript·css·学习·html·ecmascript
Agent产品评测局2 小时前
智能体在药物发现阶段如何辅助完成靶点专利覆盖的自动识别?2026药研AI Agent全景盘点与自动化选型指南
java·人工智能·ai·chatgpt·自动化
小短腿的代码世界2 小时前
Qwt性能优化与源码级深度解析:工业级图表控件的极限性能调优
开发语言·qt·信息可视化·性能优化
新新学长搞科研2 小时前
【高届数机械工程会议】第十二届机械工程、材料和自动化技术国际学术会议(MMEAT 2026)
运维·人工智能·算法·机器学习·自动化·软件工程·激光
狐璃同学2 小时前
数据结构(2)线性表
数据结构·算法
lsx2024062 小时前
jQuery UI 实例
开发语言
啦啦啦_99992 小时前
4. KNN算法之 特征预处理(归一化&标准化)
算法