【LeetCode 225】用一个or两个队列实现栈

本文介绍使用队列实现堆栈的两种方法,一种是利用两个队列,通过元素的不断移动实现堆栈操作;另一种是仅用一个队列,通过元素的反复入队和出队实现。这两种方法分别在效率和简洁性上有所侧重。

LeetCode 225:

Implement the following operations of a stack using queues.
push(x) – Push element x onto stack.
pop() – Removes the element on top of the stack.
top() – Get the top element.
empty() – Return whether the stack is empty.

要用队列实现堆栈,主要就是要实现上述的四个功能函数,这里给出两种解法,第一种是利用两个队列来实现栈的解法:

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) {
        if (this.empty()) {
            queue1.add(x);
        } else {
            if (queue1.isEmpty()) {
                queue2.add(x);
            } else {
                queue1.add(x);
            }
        }
    }

    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        int value = 0;
        if (queue1.isEmpty()) {
            while (queue2.size() > 1) {
                queue1.add(queue2.poll());
            }
            value = queue2.poll();
        } else {
            while (queue1.size() > 1) {
                queue2.add(queue1.poll());
            }
            value = queue1.poll();
        }
        return value;
    }

    /** Get the top element. */
    public int top() {
        int value = 0;
        if (queue1.isEmpty()) {
            while (queue2.size() > 1) {
                queue1.add(queue2.poll());
            }
            value = queue2.peek();
            queue1.add(queue2.poll());
        } else {
            while (queue1.size() > 1) {
                queue2.add(queue1.poll());
            }
            value = queue1.peek();
            queue2.add(queue1.poll());
        }
        return value;
    }

    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
    }
}

它的思路就是:当你要取一个元素时,相当于要取队列的最末一个元素,将队列前n - 1个元素都移动到另外一个队列中,剩下的,就是原队列中的最末一个元素,取出即使栈返回的结果。这种思路非常好,但代码可能不太简洁。

第二种,是只用一个队列的解法,稍微难想一点,但更简洁:

class MyStack {

    Queue<Integer> queue;

    /** Initialize your data structure here. */
    public MyStack() {
        queue = new LinkedList<>();
    }

    /** Push element x onto stack. */
    public void push(int x) {
        queue.add(x);
        for (int i = 0; i < queue.size() - 1; i++) {
            queue.add(queue.poll());
        }
    }

    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        return queue.poll();
    }

    /** Get the top element. */
    public int top() {
        return queue.peek();
    }

    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue.isEmpty();
    }
}

这段代码主要可能出现的疑惑在于,压入栈的部分,只要这一部分想通了,后面的部分就容易考虑了。

我们可以这么想,队列的特点是什么?一边入一边出,先入先出,而栈是先入后出,那,我们是否可以通过某些方法,让队列的一端维持“栈”的那种先入后出的状态呢?
我们可以想,如果对于两个元素:a、b,出的顺利应该为: b、a,对于三个元素,a、b、c,出栈的顺利应该为 c、b、a,从两个元素,到三个元素,我们压入了一个元素c,而我们应该维护的顺序,从"b、a",变成了压栈后"b、a、c",变成"c、b、a",可以发现,对于每进入一个元素,将前面所有元素取出再重新入队列,可以维护同栈一样的顺序,因此:

    public void push(int x) {
    	// 加入一个元素
        queue.add(x);
        // 在这个元素前的所有元素进行出队列,再入队列的操作
        for (int i = 0; i < queue.size() - 1; i++) {
            queue.add(queue.poll());
        }
    }

在LeetCode上进行执行,都可以通过:
在这里插入图片描述
用两个队列的方法更快一些,但同时占用内存的量更大。

其他 LeetCode 题目解析

<think>好的,我现在需要回答用户关于如何用两个实现队列的问题。首先,我得回顾一下队列的基本特性。是后进先出(LIFO)的,而队列是先进先出(FIFO)的。所以,用两个来模拟队列的操作,需要找到一种方法让元素的顺序反转。 我记得有两种主要的方式:一种是在入队的时候处理,另一种是在出队的时候处理。根据之前的引用资料,比如引用[2]和[5],可能需要在入队和出队时调整的结构。比如,当出队的为空时,需要将入队的所有元素转移到出队,这样顺序就反转了,可以满足队列的FIFO要求。 接下来,我需要确定具体的实现步骤。首先,定义两个一个用于入队(push),另一个用于出队(pop)。当执行入队操作时,直接将元素压入push。出队时,如果pop为空,就将push的所有元素依次弹出并压入pop,这样pop顶就是队列的队头元素,弹出即可。如果pop不为空,直接弹出顶元素。 还要考虑队列的其他操作,比如查看队头元素,判断队列是否为空,获取队列大小等。这些操作都需要同时考虑两个的状态。例如,判断队列是否为空需要两个都为空时才成立。 另外,时间复杂度方面,虽然单个出队操作可能在转移元素时是O(n),但均摊下来每个元素只会被转移一次,所以均摊时间复杂度是O(1)。这符合用户可能关心的性能问题。 还需要注意代码实现的细节,比如在C语言中可能需要手动管理的内存,而在其他高级语言中可能有更简便的方式。根据引用[3]中的示例,使用两个的结构体来管理,初始化时创建两个,并在销毁时释放内存。 可能的相关问题包括这种实现方式的时间复杂度分析、优化方法,或者与其他实现方式的比较,比如用链表实现队列。根据引用[1],链表实现队列在出队时更高效,而用数组实现队列可能在头部操作时效率较低,所以用模拟可能在某些场景下有优势。 最后,需要确保回答结构清晰,分步骤说明,并引用相关文献。同时生成的相关问题要围绕实现原理、复杂度分析以及应用场景展开,帮助用户深入理解。</think>### 如何用两个实现队列? #### 核心原理 队列是先进先出(FIFO),而是后进先出(LIFO)。通过两个的“反转”操作,可以实现队列的FIFO特性。具体步骤如下: 1. **定义两个**: - **入队(`push_stack`)**:仅处理入队操作(`enqueue`)。 - **出队(`pop_stack`)**:仅处理出队操作(`dequeue`)。 2. **入队操作**: - 直接将元素压入`push_stack`,时间复杂度为$O(1)$。 3. **出队操作**: - 若`pop_stack`为空,将`push_stack`中所有元素依次弹出并压入`pop_stack`(此时元素顺序被反转)。 - 弹出`pop_stack`的顶元素,即队列的队头元素。 - **均摊时间复杂度**:每个元素最多经历两次操作(压入`push_stack`→弹出→压入`pop_stack`→弹出),因此均摊时间复杂度为$O(1)$[^5]。 #### 代码实现(Python示例) ```python class MyQueue: def __init__(self): self.push_stack = [] self.pop_stack = [] def enqueue(self, x: int) -> None: self.push_stack.append(x) def dequeue(self) -> int: if not self.pop_stack: while self.push_stack: self.pop_stack.append(self.push_stack.pop()) return self.pop_stack.pop() def peek(self) -> int: if not self.pop_stack: while self.push_stack: self.pop_stack.append(self.push_stack.pop()) return self.pop_stack[-1] def is_empty(self) -> bool: return not (self.push_stack or self.pop_stack) ``` #### 关键分析 - **时间复杂度**: - 入队:$O(1)$。 - 出队:均摊$O(1)$,最坏情况(需反转)为$O(n)$。 - **空间复杂度**:$O(n)$,需两个存储所有元素。 #### 应用场景 - 需要队列功能但底层仅支持的库(如某些嵌入式系统)。 - 算法题中限制使用特定数据结构(如LeetCode 232题)[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值