队列实现栈的3种方法,全都击败了100%的用户!

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

本文已收录至 Github《小白学算法》系列:https://github.com/vipstone/algorith

之前我们讲过《用两个栈实现一个队列》,而今天我们要讲的是「用队列实现栈」,它们都属于常见的面试题,而我们今天要用多种方法来实现队列到栈的“转变”。

老规矩,先来回顾一下栈(Stack)和队列(Queue)的特性和常见方法。

栈是后进先出(LIFO)的数据结构,常见方法如下:

  • push():入栈方法,向栈顶添加元素;

  • pop():出栈方法,将栈顶的元素移除并返回元素;

  • peek():查询栈顶元素,并不会移除元素。

队列是先进先出(FIFO)的数据结构,常见方法如下:

  • offer():入队方法,向队尾添加元素;

  • poll():出队方法,从队头移除并返回元素;

  • peek():查询队头元素,并不会移除元素。

知道了这些基础内容之后,就来看今天的问题吧。

题目描述

使用队列实现栈的下列操作:

push(x) -- 元素 x 入栈

pop() -- 移除栈顶元素

top() -- 获取栈顶元素

empty() -- 返回栈是否为空

注意:

  • 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的;

  • 你所使用的语言也许不支持队列。你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可;

  • 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

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

题目解析

这道题的题目很好理解:只需要将先进先出的队列,转换为后进先出的“栈”就可以了,我们可以从多个方向入手来实现此功能,比如使用两个队列插入并交换的方式,或者是一个队列插入再交换的方式,或双端队列的方式来实现此功能,具体实现方法和代码如下。

实现方法 1:两个队列实现栈

之前我们用两个栈实现了一个队列的文章中,主要使用的是「负负得正」的思想,那么当看到此道题时,首先应该想到的是用两个队列来实现一个栈,但这里的实现思路和用栈实现队列的思路又略有不同,因为队列都是先进先出的,我们可以把它理解为「正数」,那么也就不能用「负负得正」的思想了,我们这里使用两个队列来实现栈,主要的操作思路是先将元素插入一个临时队列中,然后再将另一个队列的所有元素插入到临时队列的尾部,从而实现后进先出功能的,具体的实现步骤如下。

步骤一

添加首个元素,入列到临时队列 queue2

步骤二

因为正式队列中无元素,因此无需将 queue1 中的元素移动到临时队列 queue2 的尾部,直接将临时队列和正式队列互换即可:

步骤三

添加第二个元素,先入列到临时队列 queue2

步骤四

再将 queue1 中的元素移动到 queue2 的尾部,如下所示:


步骤五

再将 queue1queue2 进行互换:

步骤六

执行出队操作:

最终效果

从最终的效果图我们可以看出,通过两个队列已经实现了后进先出的特性,也就是完成了从队列到栈的转换,实现代码如下:

import java.util.Queue;class MyStack {Queue<Integer> queue1;Queue<Integer> queue2;public MyStack() {queue1 = new LinkedBlockingQueue();queue2 = new LinkedBlockingQueue();}/*** 入栈*/public void push(int x) {// 1.入列临时队列二queue2.offer(x);// 2.将队列一的所有元素移动到队列二while (!queue1.isEmpty()) {queue2.offer(queue1.poll());}// 3.队列一和队列二互换Queue<Integer> temp = queue1;queue1 = queue2;queue2 = temp;}/*** 出栈并返回此元素*/public int pop() {return queue1.poll();}/*** 查询栈顶元素*/public int top() {return queue1.peek();}/*** 判断是否为空*/public boolean empty() {return queue1.isEmpty();}
}

我们在 LeetCode 中提交以上测试代码,执行结果如下:

此方法很稳,竟然击败了 100% 的用户。

实现方法 2:一个队列实现栈

那我们可以不可以用一个队列来实现栈呢?答案是肯定的。

我们只需要执行以下两个步骤就可以实现将队列转换为栈了,具体实现步骤如下:

  1. 将元素入列到队尾;

  2. 再将除队尾之外的所有元素移除并重写入列。

这样操作之后,最后进入的队尾元素反而变成了队头元素,也就实现了后进先出的功能了,如下图所示。

步骤一

元素 1 入列:

步骤二

元素 2 入列:

步骤三

将最后一个元素之前的所有元素,也就是元素 1,出列重新入列:

步骤四

执行出队操作:

最终效果

以上思路的实现代码如下:

import java.util.Queue;class MyStack {Queue<Integer> queue1;public MyStack() {queue1 = new LinkedBlockingQueue();}/*** 入栈*/public void push(int x) {// 获取原队列长度(要移动的次数)int count = queue1.size();// 将元素放入队尾queue1.offer(x);// 将除最后一个元素外,其他的元素重新入队for (int i = 0; i < count; i++) {System.out.println("for");queue1.offer(queue1.poll());}}/*** 出栈并返回此元素*/public int pop() {return queue1.poll();}/*** 查询栈顶元素*/public int top() {return queue1.peek();}/*** 判断是否为空*/public boolean empty() {return queue1.isEmpty();}
}

我们把以上代码在 LeetCode 中提交,执行结果如下:


此方法依旧很稳,也是同样的击败了 100% 的用户,只不过此方法在内存方面有更好的表现。

实现方法 3:双端队列实现栈

如果觉得以上方法比较难的话,最后我们还有一个更简单的实现方法,我们可以使用 Java 中的双端队列 ArrayDeque 来实现将元素可以插入队头或队尾,同样移除也是,那么这样我们就可以从队尾入再从队尾出,从而就实现了栈的功能了。

双端队列结构如下:


我们来演示一下用双端队列实现栈的具体步骤。

步骤一

元素 1 入队:

步骤二

元素 2 入队(队尾):

步骤三

再从队尾出队:

最终效果

以上思路的实现代码如下:

import java.util.ArrayDeque;class MyStack {ArrayDeque<Integer> deque;public MyStack() {deque = new ArrayDeque<>();}/*** 入栈*/public void push(int x) {deque.offer(x);}/*** 出栈并返回此元素*/public int pop() {return deque.pollLast();}/*** 查询栈顶元素*/public int top() {return empty() ? -1 : deque.peekLast();}/*** 判断是否为空*/public boolean empty() {return deque.isEmpty();}
}

我们把以上代码在 LeetCode 中提交,执行结果如下:

总结

本文我们用 3 种方法实现了将队列转换为栈,其中最简单的方法是用 Java 中自带的双端队列 ArrayDeque 从队尾入并从队尾出就实现了栈 ,其他两个方法使用的是普通队列,通过入队之后再移动元素到入队元素之后的方法,从而实现了栈的功能。

小伙伴们,你学会了吗?


往期推荐

算法图解:如何用两个栈实现一个队列?


真不错,图解Java中的5大队列!


小白学算法:买卖股票的最佳时机!


关注我,每天陪你进步一点点!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545277.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java类类getConstructor()方法及示例

类的类getConstructor()方法 (Class class getConstructor() method) getConstructor() method is available in java.lang package. getConstructor()方法在java.lang包中可用。 getConstructor() method is used to return a Constructor object that reflects the given pub…

where、having、group by、order by、count的使用注意

where、having、group by、order by、count的使用注意1_where、having、group by、order by的顺序2_group by的作用3_where和group by的组合4_group by和having的组合5_where、having、group by的组合使用6_count与group by的组合使用1_where、having、group by、order by的顺序…

Oracle备份还原

Oracle有两类备份方式&#xff1a; &#xff08;1&#xff09;物理备份&#xff1a;是将实际组成数据库的操作系统文件从一处拷贝到另一处的备份过程&#xff0c;通常是从磁盘到磁带。物理备份又分为冷备份、热备份&#xff1b; &#xff08;2&#xff09;逻辑备份&#xff1…

Eclipse快捷键-方便查找

【Ct rlT】 搜索当前接口的实现类 1. 【ALT /】 此快捷键为用户编辑的好帮手&#xff0c;能为用户提供内容的辅助&#xff0c;不要为记不全方法和属性名称犯愁&#xff0c;当记不全类、方法和属性的名字时&#xff0c;多体验一下【ALT /】快捷键带来的好处吧。 2. 【Ct rlO】…

很实用的21个SQL小技巧!

前言每一个好习惯都是一笔财富&#xff0c;本文基于MySQL&#xff0c;分SQL后悔药&#xff0c; SQL性能优化&#xff0c;SQL规范优雅三个方向&#xff0c;分享写SQL的21个好习惯&#xff0c;谢谢阅读&#xff0c;加油哈~1. 写完SQL先explain查看执行计划&#xff08;SQL性能优化…

Java ByteArrayOutputStream size()方法与示例

ByteArrayOutputStream类size()方法 (ByteArrayOutputStream Class size() method) size() method is available in java.io package. size()方法在java.io包中可用。 size() method is used to return the current size of the buffer exists. size()方法用于返回缓冲区当前存…

Oracle备份文件名获取系统时间的做法(windows)

在对数据库进行备份时&#xff0c;用数据泵的方法&#xff0c;需要执行一段代码&#xff1a; --建目录 create directory dir as d:\bak;--给用户授权使用目录 grant read,write on directory dir to scott;--在cmd中输入以下代码 expdp scott/tigerXE directorydir dumpfileab…

java getmonth_Java LocalDateTime类| 带示例的getMonth()方法

java getmonthLocalDateTime类getMonth()方法 (LocalDateTime Class getMonth() method) getMonth() method is available in java.time package. getMonth()方法在java.time包中可用。 getMonth() method is used to get the field value month-of-year based on the enum Mon…

阿里最喜欢问的多线程顺序打印的5种解法!

Keeper导读大家在换工作面试中&#xff0c;除了一些常规算法题&#xff0c;还会遇到各种需要手写的题目&#xff0c;所以打算总结出来&#xff0c;给大家个参考。全文 2929 字&#xff0c;剩下的是代码&#xff0c;P6 及以下阅读只需要 8 分钟&#xff0c;高 P 请直接关闭第一篇…

CSS入门

CSS入门1_CSS概要1.1_CSS引入方式2_CSS选择器3_字体样式3.1_字体属性3.2_字体类型&#xff1a;font-family3.3_字体大小&#xff1a;font-size3.4_字体粗细&#xff1a;font-weight3.5_字体颜色&#xff1a;color3.6_总结4_文本样式4.1_文本样式属性4.2_首行缩进&#xff1a;te…

Oracle笔记:数据库启动的三个阶段

数据库的启动可分为三个阶段&#xff1a;1、startup nomount -- 启动实例&#xff0c;不加载数据库 nomount&#xff1a;在这一阶段&#xff0c;只需要读取initSID.ora文件&#xff0c;启动数据库实例&#xff0c;创建后台进程。在initSID.ora文件中&#xff0c;可以定位…

Java LocalDate类| lengthOfYear()方法和示例

LocalDate类lengthOfYear()方法 (LocalDate Class lengthOfYear() method) lengthOfYear() method is available in java.time package. lengthOfYear()方法在java.time包中可用。 lengthOfYear() method is used to get the length of the year represented by this LocalDate…

23张图!万字详解「链表」,从小白到大佬!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;链表和数组是数据类型中两个重要又常用的基础数据类型。数组是连续存储在内存中的数据结构&#xff0c;因此它的优势是可以通…

何为“秒传”

写在前面 最近一直在弄文件传输的组件&#xff0c;在讨论组里面&#xff0c;有同事提到“秒传”的功能。在目前的通信软件中也有网盘的功能&#xff0c;就从网上搜了一下&#xff0c;这里对“秒传”的实现思路做一下总结&#xff0c;在之后会写一个小的demo实现一下。没有其他&…

JavaScript入门

JavaScript入门1_语法基础1.1_变量1.2_数据类型1.3_运算符1.4_类型转换1.5_注释跟c一样2_流程控制语句3_函数3.1_没有返回值的函数3.2_有返回值的函数4_对象4.1_对象简介4.2_字符串对象4.3_数组对象4.4_时间对象4.5_数学对象简介5_DOM基础5.1_DOM是什么5.2_节点类型5.3_获取元素…

oracle 序列的概念与使用步骤

转载自&#xff1a;http://www.worlduc.com/blog2012.aspx?bid20342458 一、概念 1、 序列: 是oacle提供的用于产生一系列唯一数字的数据库对象。主要用于提供主键值。 2、 创建序列&#xff1a; 创建序列的语法 CREATE SEQUENCE sequence //创建序列名称 [INCREMENT BY n]…

数组转List的3种方法和使用对比!

作者 | 大脑补丁来源 | blog.csdn.net/x541211190/article/details/79597236前言&#xff1a;本文介绍Java中数组转为List三种情况的优劣对比&#xff0c;以及应用场景的对比&#xff0c;以及程序员常犯的类型转换错误原因解析。一.最常见方式&#xff08;未必最佳&#xff09;…

java 根据类名示例化类_Java即时类| getEpochSecond()方法与示例

java 根据类名示例化类即时类getEpochSecond()方法 (Instant Class getEpochSecond() method) getEpochSecond() method is available in java.time package. getEpochSecond()方法在java.time包中可用。 getEpochSecond() method is used to get the number of seconds from t…

oracle10g备份导入

2019独角兽企业重金招聘Python工程师标准>>> //导出 exp test/testgdsoft filed:\gd.dmp //删用户 drop user wkwx cascade; //用PLSQL创建数据数据库 create user hzjzjn identified by hzjzjn default tablespace BDP_DATABD; grant CREATE USER,DROP USER,ALTE…

VisualSVNServer的使用

VisualSVNServer的使用1_服务端初识1.1_创建新仓库1.2_创建用户并分配权限1_服务端初识 1.1_创建新仓库 右击Repository&#xff0c;点击create 点击下一步 输入仓库名 右击空白处&#xff0c;点击新建&#xff0c;点击project structure 输入工程名 1.2_创建用户并分…