队列和栈——如何相互实现

01栈和队列相互实现

栈和队列两种不同的数据结构,是相反的存取特性。栈遵循后进先出(LIFO)原则,队列遵循先进先出(FIFO)原则。

本文介绍如何用栈实现队列,以及如何用队列实现栈,这是数据结构的经典问题。

02栈实现队列

问题描述

LeetCode 232: https://leetcode.cn/problems/implement-queue-using-stacks/

给定只能使用标准栈结构,要求实现一个完整的队列功能,包括入队、出队、查看队头元素和判断空等操作。

核心思路

使用两个栈配合工作:

- in栈:负责接收新加入的元素

- out栈:负责提供队列的出队操作

关键在于数据转移的两个原则:

1. 转移条件:只有当out栈为空时,才能从in栈转移数据

2. 转移完整性:如果要转移数据,必须将in栈中的所有数据全部转移完

实现代码

    /** * 用栈实现队列 */class StackToQueue {    private Stack<Integer> inStack;    // 负责入队操作    private Stack<Integer> outStack;   // 负责出队操作        public StackToQueue() {        inStack = new Stack<>();        outStack = new Stack<>();    }        /**     * 数据转移方法     * 核心原则:     * 1. 只有outStack为空时才能转移     * 2. 转移时必须将inStack清空     */    private void transferData() {        if (outStack.isEmpty()) {            while (!inStack.isEmpty()) {                outStack.push(inStack.pop());            }        }    }        /**     * 入队操作     * @param x 要入队的元素     */    public void push(int x) {        transferData();  // 先尝试转移数据        inStack.push(x); // 新元素总放入inStack    }        /**     * 出队操作     * @return 队头元素     */    public int pop() {        transferData();     // 先确保outStack弹出所有数据,再转移        return outStack.pop();    }        /**     * 查看队头元素(不弹出)     * @return 队头元素     */    public int peek() {        transferData();        return outStack.peek();    }        /**     * 判断队列是否为空     */    public boolean empty() {        return inStack.isEmpty() && outStack.isEmpty();    }}

    时间复杂度(均摊):

    - push():O(1) - 均摊分析,每个元素最多进出栈4次

    - pop():O(1) - 均摊分析

    - peek():O(1) - 均摊分析

    - empty():O(1) - 直接判断

    空间复杂度:

    - O(n) - 需要两个栈存储n个元素

    03队列实现栈

    问题描述

    LeetCode 225: https://leetcode.cn/problems/implement-stack-using-queues/ 

    给定只能使用标准队列结构,要求实现一个完整的栈功能,包括压栈、弹栈、查看栈顶元素和判断空等操作。

    核心思路

    使用一个队列即可实现。关键思想是在每次压栈时:

    1. 记录新元素加入前队列的大小n

    2. 将新元素加入队尾

    3. 执行n次"队头出队→队尾入队"操作,将新元素调整到队头位置

    这样队头始终是最后压入的元素,符合栈的后进先出特性。

    实现代码

      class QueueToStack {    private Queue<Integer> queue;        public QueueToStack() {        queue = new LinkedList<>();    }        /**     * 压栈操作     * 时间复杂度:O(n),因为重新排列队列中的n个元素     * @param x 要压入的元素     */    public void push(int x) {        int size = queue.size();  // 记录加入新元素前的队列大小        queue.offer(x);           // 新元素入队                // 将之前的所有元素重新排列,使新元素位于队头        for (int i = 0; i < size; i++) {            queue.offer(queue.poll());        }    }        /**     * 弹栈操作     */    public int pop() {        return queue.poll();    }        /**     * 查看栈顶元素(不弹出)     * @return 栈顶元素     */    public int top() {        return queue.peek();    }        /**     * 判断栈是否为空     */    public boolean empty() {        return queue.isEmpty();    }}

      时间复杂度:

      - push():O(n) - 需要重新排列队列中的n个元素

      - pop():O(1) - 直接从队头弹出

      - top():O(1) - 直接访问队头

      - empty():O(1) - 直接判断

      空间复杂度:

      - O(n) - 需要一个队列存储n个元素

      04实现难度分析

      栈实现队列

      1. 数据转移时机:只在out为空时转移

      2. 转移完整性:转移时必须清空in栈

      3. 所有操作均摊时间复杂度为O(1)

      队列实现栈

      1. 元素重排:新元素入队后立即重排到队头

      2. 大小记录:准确记录重排前的队列大小,重排操作次数等于原队列大小

      3. 压栈操作时间复杂度为O(n),性能较差,适用压栈操作较少的场合

      文章参考:bilibili左程云算法系列

      青轴作响,叨叨有声。

      欢迎留言,一起分享你的技术见闻。每一篇都聊技术人的真实选择与成长。IT之路,不孤单。

      声明:文章仅代表作者本人观点,供学习和交流使用。

      公众号:叨叨猿的青轴日记

      更多往期内容请在公众号回复[算法那些事]

      合作/投稿:daodaoyuanblue@163.com

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

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

      抵扣说明:

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

      余额充值