数据结构:使用队列实现栈

💬 前言

本文主要是记录我在Leetcode上对题号225——队列实现栈的解题过程。

🏷 简介

本题要求使用两个队列实现一个后入先出(LIFO)的栈(MyStack类),并支持普通队列的全部四种操作(pushtoppopempty)。

所以全文中心点是使用队列实现栈操作

1. 思路一: 双队列实现栈

1.1 核心思想

使用两个队列,一个为主队列,名为队列一;另一个为主队列的缓存队列,名为队列二。

当进行栈的出栈时,将队列一里除末尾元素外其他元素出队并缓存至队列二,然后将末尾元素单独拉出来出队并return,完成后将缓存在队列二里的非末尾元素恢复至队列一,从而实现了和栈操作相同的效果。
在这里插入图片描述

1.2 具体实现

既然是用队列实现,首先就是要开一个队列的成员变量空间:

private Queue<Integer> queue;

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

在这里我们使用Java自带的LinkedList来实现队列,LinkedList也就是链表。

然后将栈的empty方法实现,非常简单,使用队列自带的操作即可:

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

MyStack的入栈操作,也就等于将元素推入队列:

public void push(int x) {
    queue.add(x);
}

出栈操作在队列的实现上有点复杂,但并不难。需要先通过循环操作将末尾元素外的其他元素都出队并缓存到队列二里面,然后再把最后的元素单独拿出来出队,所以实际上真正出队的只有末尾元素;最后再将缓存在队列二里的非末尾元素重新放入主队列:

public int pop() {
    // 创建缓存队列 queue2
    Queue<Integer> queue2 = new LinkedList<>(); 
    
    // 除了最后一个元素,将 queue 中的所有元素放入 queue2
    while (queue.size() > 1) {
        queue2.add(queue.remove());
    }

    // 此时 queue 中剩下的最后一个元素就是「栈顶」元素
    int ret = queue.remove();  
    
    // 此时 queue2 缓存的数据赋值给 queue
    queue = queue2;
  
    return ret;  
}

一旦我们实现了 pop()方法,那么实现 top() 就简单了。我们可以复用我们已经实现的 pop(),将栈顶元素拿出来,记录下来,作为返回值。然后因为 top() 不删除元素,我们再将这个值放进队列就好了。

public int top() {
    int ret = pop();
    push(ret);
    return ret;
}

1.3 复杂度分析

方法具体值备注
push()O(1)向队列enqueue()
pop()O(n)因循环整个队列,直至取到最后一个元素,所以复杂度是O(n)级
top()O(n)因为复用了pop()方法,复杂度也是O(n)级
empty()O(1)复用队列自带的isEmpty()方法

2.思路二:单队列实现栈

2.1 核心思想

在向队列推入元素时,同时将队列除末尾元素外的其他元素都出队并再入队,将整个队列倒置过来,这样子最新入队的元素永远是队首位,在出队时也将必是队首,从而达到和栈操作一样的后进先出的效果。
在这里插入图片描述

2.2 具体实现

本思路相较思路一代码简洁,复杂度也降低了,是一种良好的优化写法。步骤简单,所以只用代码说明。

public class MyStack {

    private Queue<Integer> queue;

    public MyStack() {
        queue = new LinkedList<Integer>();
    }

    public void push(int x) {
      	//	入队
        queue.offer(x);
        for (int i = 0;i < queue.size() - 1;i++) {
            //	再将非末尾的元素一一出队并再入队到末尾
          	//	如此一来,最先入队的元素总在队首位,所以也会最先出队,从而达到后进先出
            queue.offer(queue.poll());
        }
    }

    public int pop() {
        return queue.poll();
    }

    public int top() {
        return queue.peek();
    }

    public boolean empty() {
        return queue.isEmpty();
    }

}

3.2 复杂度分析

方法具体值备注
push()O(n)承担了倒置队列的职责,所以复杂度也上升了
pop()O(1)复用队列的poll()方法
top()O(1)复用队列的peek()方法
empty()O(1)复用队列的isEmpty()方法

📝 3. 总结

  • 思路二在push()阶段倒置队列,将最新加入的元素放到了队首,从而能起到后进先出( LIFO )的效果。相应的,其他栈操作方法也只需要调用队列的对应方法,时间复杂度降低到了O( 1 )。
  • 思路一把主要操作在pop()方法里,特别是top()方法还复用了最复杂的pop()方法,整体复杂度有一定上升。
  • 思路二较思路一在想法上简单,在实现上也不用多写复杂的代码,整体复杂度上也低了不少。所以更推荐使用方法二。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值