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

一、核心原理讲解

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)的原因:每个元素仅被倒腾/迁移一次,后续操作无需重复处理。
相关推荐
rit84324999 分钟前
MATLAB近红外光谱预处理:平滑与求导(MSV方法)
数据结构·算法·matlab
蚂蚁数据AntData14 分钟前
从ChatBI到业务记忆:重新定义数据智能的生产力边界
大数据·网络·数据库·人工智能·算法
_日拱一卒15 分钟前
LeetCode:22括号生成
算法·leetcode·职场和发展
cfm_291416 分钟前
JVM垃圾收集算法与收集器深度解析
jvm·测试工具·算法·性能优化
郝学胜_神的一滴25 分钟前
干货版《算法导论》09:让哈希表稳如泰山的终极解法
数据结构·算法
CC数学建模31 分钟前
2026年第十六届APMCM 亚太地区大学生数学建模竞赛(中文赛项)赛题C题:创业社区规划与资源配置优化问题完整思路、代码、模型、文章,全网首发高质量分享!
python·算法·数学建模
徐小夕33 分钟前
我们放弃了单Agent方案:HiCAD 3.0 用 Harness 做多Agent编排,把3D建模的准确率提升了30%
前端·算法·github
洛水水37 分钟前
【力扣100题】88.多数元素
数据结构·算法·leetcode
Shan120543 分钟前
无向图的Hierholzer算法流程(一)
算法
一切皆是因缘际会1 小时前
频域特征解构底层机理与双域融合鉴伪算法优化
人工智能·算法·ai·架构