双栈实现队列/双队列实现栈

一、核心原理讲解

1. 双栈实现队列

队列的核心是先进先出(FIFO) ,栈是后进先出(LIFO)。利用两个栈的"元素倒腾"特性,将栈的LIFO转换为队列的FIFO:

  • 入栈(inStack):专门接收入队元素,保证入队操作O(1)。
  • 出栈(outStack):专门弹出队首元素;若出栈为空,将入栈的所有元素弹出并压入出栈(此时入栈的栈底元素变为出栈的栈顶,符合FIFO)。
  • 每个元素最多被倒腾一次,均摊时间复杂度O(1)。
2. 双队列实现栈

栈的核心是后进先出(LIFO) ,队列是先进先出(FIFO)。利用两个队列的"元素迁移"特性,将队列的FIFO转换为栈的LIFO:

  • 主队列(queue1):保存栈的所有元素,入栈操作直接添加到主队列,O(1)。
  • 辅助队列(queue2):临时存储迁移元素;出栈时将主队列除最后一个元素外的所有元素移到辅助队列,主队列剩余的最后一个元素即为栈顶,弹出后交换主/辅队列引用。
  • 每个元素最多被迁移一次,均摊时间复杂度O(1)。

二、代码实现(每行注释+详解)

1. 双栈实现队列(StackQueue)
java 复制代码
import java.util.Deque;
import java.util.LinkedList;

/**
 * 双栈实现队列
 * 核心:入栈存元素,出栈弹队首;出栈空时将入栈元素倒腾到出栈
 */
public class StackQueue {
    // 入栈:接收入队元素(用Deque+LinkedList,替代不推荐的Stack类)
    private final Deque<Integer> inStack;
    // 出栈:弹出队首元素
    private final Deque<Integer> outStack;

    /**
     * 构造方法:初始化两个栈
     */
    public StackQueue() {
        inStack = new LinkedList<>(); // LinkedList实现Deque,支持栈操作
        outStack = new LinkedList<>();
    }

    /**
     * 入队操作:元素压入入栈
     * @param x 入队元素
     */
    public void offer(int x) {
        inStack.push(x); // 栈顶压入,O(1)
    }

    /**
     * 出队操作:弹出队首元素
     * @return 队首元素
     * @throws IllegalStateException 队列为空时抛异常
     */
    public int poll() {
        // 出栈为空时,将入栈所有元素倒腾到出栈
        if (outStack.isEmpty()) {
            while (!inStack.isEmpty()) {
                // 入栈弹出元素,压入出栈(反转元素顺序)
                outStack.push(inStack.pop());
            }
        }
        // 倒腾后仍为空,说明队列为空
        if (outStack.isEmpty()) {
            throw new IllegalStateException("队列已空,无法出队");
        }
        // 弹出出栈栈顶(即队首)
        return outStack.pop();
    }

    /**
     * 获取队首元素(不弹出)
     * @return 队首元素
     * @throws IllegalStateException 队列为空时抛异常
     */
    public int peek() {
        // 逻辑同poll,仅不弹出元素
        if (outStack.isEmpty()) {
            while (!inStack.isEmpty()) {
                outStack.push(inStack.pop());
            }
        }
        if (outStack.isEmpty()) {
            throw new IllegalStateException("队列已空,无法获取队首");
        }
        // 获取出栈栈顶(队首)
        return outStack.peek();
    }

    /**
     * 判断队列是否为空
     * @return 空返回true,否则false
     */
    public boolean isEmpty() {
        // 两个栈都为空,队列才为空(元素可能分布在任意栈)
        return inStack.isEmpty() && outStack.isEmpty();
    }

    // 测试方法
    public static void main(String[] args) {
        StackQueue queue = new StackQueue();
        queue.offer(1);  // inStack: [1]
        queue.offer(2);  // inStack: [2,1]
        queue.offer(3);  // inStack: [3,2,1]
        
        System.out.println(queue.poll());  // 倒腾inStack到outStack → outStack: [1,2,3],弹出1 → 输出1
        System.out.println(queue.peek());  // outStack: [2,3],获取栈顶2 → 输出2
        
        queue.offer(4);  // inStack: [4]
        System.out.println(queue.poll());  // 弹出outStack栈顶2 → 输出2
        System.out.println(queue.poll());  // 弹出outStack栈顶3 → 输出3
        System.out.println(queue.poll());  // outStack空,倒腾inStack的4 → 弹出4 → 输出4
        System.out.println(queue.isEmpty()); // 两个栈都空 → 输出true
    }
}
2. 双队列实现栈(QueueStack)
java 复制代码
import java.util.LinkedList;
import java.util.Queue;

/**
 * 双队列实现栈
 * 核心:主队列存元素,出栈时迁移主队列元素到辅助队列,保留最后一个元素为栈顶
 */
public class QueueStack {
    // 主队列:保存栈的所有元素(非final,需交换引用)
    private Queue<Integer> queue1;
    // 辅助队列:临时存储迁移的元素
    private Queue<Integer> queue2;

    /**
     * 构造方法:初始化两个队列
     */
    public QueueStack() {
        queue1 = new LinkedList<>(); // LinkedList实现Queue,支持队列操作
        queue2 = new LinkedList<>();
    }

    /**
     * 入栈操作:元素添加到主队列
     * @param x 入栈元素
     */
    public void push(int x) {
        queue1.offer(x); // 队列尾部添加,O(1)
    }

    /**
     * 出栈操作:弹出栈顶元素(最后入栈的元素)
     * @return 栈顶元素
     * @throws IllegalStateException 栈为空时抛异常
     */
    public int pop() {
        // 主队列为空,栈为空
        if (queue1.isEmpty()) {
            throw new IllegalStateException("栈已空,无法出栈");
        }
        // 迁移主队列除最后一个元素外的所有元素到辅助队列
        while (queue1.size() > 1) {
            queue2.offer(queue1.poll());
        }
        // 主队列剩余的唯一元素即为栈顶
        int top = queue1.poll();
        // 交换主/辅队列引用(辅助队列变主队列,原主队列变空辅助队列)
        Queue<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
        // 返回栈顶元素
        return top;
    }

    /**
     * 获取栈顶元素(不弹出)
     * @return 栈顶元素
     * @throws IllegalStateException 栈为空时抛异常
     */
    public int peek() {
        if (queue1.isEmpty()) {
            throw new IllegalStateException("栈已空,无法获取栈顶");
        }
        // 迁移主队列除最后一个元素外的所有元素到辅助队列
        while (queue1.size() > 1) {
            queue2.offer(queue1.poll());
        }
        // 获取栈顶元素(主队列唯一元素)
        int top = queue1.peek();
        // 栈顶元素也移到辅助队列(避免丢失)
        queue2.offer(queue1.poll());
        // 交换队列引用,恢复主队列
        Queue<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
        return top;
    }

    /**
     * 判断栈是否为空
     * @return 空返回true,否则false
     */
    public boolean isEmpty() {
        // 主队列为空,栈即为空(辅助队列仅临时存储)
        return queue1.isEmpty();
    }

    // 测试方法
    public static void main(String[] args) {
        QueueStack stack = new QueueStack();
        stack.push(1);  // queue1: [1]
        stack.push(2);  // queue1: [1,2]
        stack.push(3);  // queue1: [1,2,3]
        
        System.out.println(stack.pop());  // 迁移1、2到queue2 → queue1: [3],弹出3 → 输出3;交换后queue1: [1,2]
        System.out.println(stack.peek()); // 迁移1到queue2 → queue1: [2],获取2;移2到queue2 → 交换后queue1: [1,2] → 输出2
        
        stack.push(4);  // queue1: [1,2,4]
        System.out.println(stack.pop());  // 迁移1、2到queue2 → queue1: [4],弹出4 → 输出4;交换后queue1: [1,2]
        System.out.println(stack.pop());  // 迁移1到queue2 → queue1: [2],弹出2 → 输出2;交换后queue1: [1]
        System.out.println(stack.pop());  // queue1: [1],弹出1 → 输出1;交换后queue1: []
        System.out.println(stack.isEmpty()); // queue1为空 → 输出true
    }
}

三、关键总结

实现方式 核心思想 入操作时间复杂度 出操作均摊时间复杂度 空间复杂度
双栈队列 入栈存、出栈弹,空则倒腾 O(1) O(1) O(n)
双队列栈 主队列存、辅队列迁,保留最后一个 O(1) O(1) O(n)
  • 双栈队列的核心是元素倒序:通过两个栈的反转,将栈的LIFO转换为队列的FIFO。
  • 双队列栈的核心是元素迁移:通过辅助队列过滤出最后入队的元素,实现栈的LIFO。
  • 均摊时间复杂度为O(1)的原因:每个元素仅被倒腾/迁移一次,后续操作无需重复处理。
相关推荐
万少6 分钟前
Qoder sonus模型 20分钟,复刻 马斯克 飞天汽车
前端·后端·算法
七牛云行业应用28 分钟前
Muse Spark Open 发布:Meta 称其为美国最强开放权重模型,816B 参数如何落地
算法
esop轻Mes看板亮灯分拣ESD静电安1 小时前
产线“最后一米”的破局密码:ESOP与MES的区别与深度协同
算法
陈天伟教授1 小时前
《数据处理和标注技术》课程能力图谱
人工智能·算法
LMT_Lanmantech1 小时前
HyperWorks许可:almutil显示Unit满了,数据到底准不准?
运维·服务器·算法
禁默1 小时前
论文解读与思考:基于场所地图的标识感知机器人探索方法及工程参考的价值
人工智能·深度学习·算法·机器人
维克兜率天1 小时前
【维克】统计套利建模:从因子到信号的完整流程
android·开发语言·python·算法·kotlin
Zentceh1 小时前
PixelClean 这类 AI 降噪算法是怎么工作的?从 RAW 域到 GAN 色彩重建的技术拆解
图像处理·人工智能·科技·算法·计算机视觉·车载系统·智能硬件
用户0390614707191 小时前
无感观测器理论模型(NTSM-SMO-PLL 与 SMO-PLL)
算法
垆边人似月.2 小时前
找出最小的 K 个数
数据结构·c++·算法