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

一、核心原理讲解

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)的原因:每个元素仅被倒腾/迁移一次,后续操作无需重复处理。
相关推荐
AI科技星2 小时前
张祥前统一场论电荷定义方程分析报告
开发语言·经验分享·线性代数·算法·数学建模
Swift社区2 小时前
LeetCode 460 - LFU 缓存
算法·leetcode·缓存
程芯带你刷C语言简单算法题2 小时前
Day39~实现一个算法确定将一个二进制整数翻转为另一个二进制整数,需要翻转的位数
c语言·开发语言·学习·算法·c
zcbdandan3 小时前
JNA内存对齐导致的结构体数组传输错误
数据结构·算法
dundunmm3 小时前
【每天一个知识点】YOLO算法
算法·yolo·目标检测
lihihi3 小时前
P5182 棋盘覆盖
算法·图论
白云千载尽3 小时前
LLaMA-Factory 入门(一):Ubuntu20 下大模型微调与部署
人工智能·算法·大模型·微调·llama
yugi9878383 小时前
基于Takens嵌入定理和多种优化算法的混沌序列相空间重构MATLAB实现
算法·matlab·重构
Yuer20253 小时前
为什么要用rust做算子执行引擎
人工智能·算法·数据挖掘·rust