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

作者 | 王磊

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

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

本文已收录至 https://github.com/vipstone/algorithm 《算法图解》系列。

队列和栈是计算机中两个非常重要的数据结构,经过前面的学习(《队列》、《栈》)我们知道了它们各自的特点,队列是先进先出(FIFO)的,而栈是先进后出(FILO)的,那如何用栈来实现队列呢?这可是一道经典的面试题,所以本文我们就来实现一下。

在正式开始之前,我们先来回顾一下栈和队列的常用方法。

栈(Stack)的常用方法包含以下这些:

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

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

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


队列(Queue)的常用方法包含以下这些:

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

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

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

有了这些前置知识,接下来我们来看今天的题目。

题目描述

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead,分别完成在队列尾部插入整数和在队列头部删除整数的功能,若队列中没有元素,deleteHead 操作返回 -1。

示例 1:

输入:

["CQueue","appendTail","deleteHead","deleteHead"]

[[],[3],[],[]]

输出:[null,null,3,-1]

示例 2:

输入:

["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]

[[],[],[5],[2],[],[]]

输出:[null,-1,null,null,5,2]

提示:

1 <= values <= 10000

最多会对 appendTail、deleteHead 进行 10000 次调用

leetcode:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/

解题思路

这道题目的意思其实很好理解,就是要将先进后出的栈改为先进先出的队列,其实问题中也给出了一些提示,“用两个栈来实现一个队列”。这道题实现的核心思想就是「负负得正」,我们先用一个栈来存入元素(这时最先进入的元素在栈底),然后再将第一个栈中的元素移动到新栈中,此时最先进入的元素就在栈顶了,然后在用第二个栈出栈时,整个执行的顺序就变成了先进先出。

接下来,我们用图解的方式来实现一下整个流程。

步骤一

先将元素入栈到第一个栈中,如下图所示:

步骤二

将第一个栈中的元素都移动到第二个栈中,如下图所示:

步骤三

所有元素从第二个栈中出栈,如下图所示:

小结

从上述图片可以看出,元素添加顺序是 1、2、3,最终经过两个栈之后的出栈顺序也是 1、2、3,这样我们就通过两个栈实现了队列(先进先出)。

实现代码

接下来我们就用代码来实现一下以上思路:

class CQueue {Stack<Integer> inputStack; // 入栈的容器(添加时操作)Stack<Integer> outputStack; // 出栈和查询的栈容器public CQueue() {inputStack = new Stack();outputStack = new Stack();}// 添加操作public void appendTail(int value) {inputStack.push(value);}// 删除操作public int deleteHead() {if (!outputStack.isEmpty()) {// 出栈容器不为空return outputStack.pop();} else if (!inputStack.isEmpty()) {// 入栈 stack 全部转移到出栈 stackwhile (!inputStack.isEmpty()) {outputStack.push(inputStack.pop());}}return outputStack.isEmpty() ? -1 : outputStack.pop();}
}

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

注意事项

在整个实现过程中有两个小细节需要特别注意一下:

  1. 第 1 个栈只负责入栈(暂存数据),第 2 个栈只负责出栈(最终的队列执行顺序);

  2. 每次栈 2 出栈时都要把所有的元素都出完之后,才能从栈 1 中追加(添加)新数据,当栈 2 的数据没有全部出栈完成时,不能将栈 1 的元素入栈到栈 2,这样会导致元素的执行顺序混乱。

总结

本文我们通过两个先进后出的栈,通过“负负得正”的思路实现了队列先进先出的特性,但需要特别注意的是当第 2 个栈也就是出栈容器,在非空(栈)时不能将第 1 个栈中的元素添加到第 2 个栈中,以免造成程序执行顺序混乱。


往期推荐

Java中的5大队列,你知道几个?

2020-10-24

一文详解「队列」,手撸队列的3种方法!

2020-10-21

动图演示:手撸堆栈的两种实现方法!

2020-09-23

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

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

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

相关文章

「递归算法」看这一篇就够了|多图

前言递归是一种非常重要的算法思想&#xff0c;无论你是前端开发&#xff0c;还是后端开发&#xff0c;都需要掌握它。在日常工作中&#xff0c;统计文件夹大小&#xff0c;解析xml文件等等&#xff0c;都需要用到递归算法。它太基础太重要了&#xff0c;这也是为什么面试的时候…

linux——服务器与客户端实现聊天功能

先联想一下聊天的场景&#xff0c;假设甲和乙在聊天&#xff0c;他们每个人都能够发送给对方一句话甚至多句话&#xff0c;也能接收到对方发来的一句或多句话&#xff0c;也就是说&#xff0c;甲在发送一句话给乙的时候&#xff0c;同时也能接收到乙发来的信息&#xff0c;而且…

有关链表的小技巧,我都给你总结好了

链表链表是数据结构里一个很基础但是又很爱考的线性结构&#xff0c;链表的操作相对来说比较简单&#xff0c;但是非常适合考察面试者写代码的能力&#xff0c;以及对 corner case 的处理&#xff0c;还有指针的应用很容易引起 NPE (null pointer exception)。综合以上原因&…

ActiveReports 9实战教程(1): 手把手搭建环境Visual Studio 2013 社区版

ActiveReports 9刚刚发布3天&#xff0c;微软就发布了 Visual Studio Community 2013 开发环境。Visual Studio Community 2013 提供完整功能的 IDE &#xff0c;可开发 Windows、Android 和 iOS 应用。支持&#xff1a;C, Python, HTML5, JavaScript, 和 C#,VB, F# 语言的开发…

第 1-1 课:Java 程序是如何执行的?

了解任何一门语言的精髓都是先俯览其全貌&#xff0c;从宏观的视角把握全局&#xff0c;然后再深入每个知识点逐个击破&#xff0c;这样就可以深入而快速的掌握一项技能。同样学习 Java 也是如此&#xff0c;本节就让我们先从整体来看一下 Java 中的精髓。 Java 介绍 Java 诞…

linux——两个客户端之间实现聊天(TCP、单线程)

两个客户端实现聊天功能&#xff0c;那么服务器作转发信息的作用&#xff0c;客户端A先将信息发送到服务器&#xff0c;在由服务器将信息发送到客户端B&#xff0c;客户端B也是一样。客户端与服务器都应该有两个执行流&#xff0c;服务器的一个执行流不断的接收客户端A的信息并…

zabbix邮件通知,短信通知配置详解

一、使用邮件发送报警1、前提条件是zabbix我们已经安装完成2、在官网我们下载msmtp的文件http://sourceforge.net/projects/msmtp/files/msmtp/1.4.32/msmtp-1.4.32.tar.bz2/download tar xf msmtp-1.4.32.tar.bz2 cd msmtp-1.4.32 ./configure--prefix/usr/local/msmtp make m…

linux——客户端服务器文件传输

实现文件传输并不难&#xff0c;只需用fopen、fread、fwrite、fclose这几个函数对文件操作即可。文本文件就不说了&#xff0c;我们就已下图为例。 我们先来看看这个图片文件里装的是什么&#xff0c;我们以notpad打开这个图片&#xff0c;结果如下&#xff0c;是一堆乱码。 …

第 1-2 课:你不知道的基础数据类型和包装类 + 面试题

基本数据类型 Java 基础数据按类型可以分为四大类&#xff1a;布尔型、整数型、浮点型、字符型&#xff0c;这四大类包含 8 种基础数据类型。 布尔型&#xff1a;boolean整数型&#xff1a;byte、short、int、long浮点型&#xff1a;float、double字符型&#xff1a;char 八种…

php中socket的使用

一、开启socket phpinfo();查看是否开启了socket扩展&#xff0c;否则在php.ini中开启。 二、服务器端代码的写法 <?php error_reporting(E_ALL); set_time_limit(0); //ob_implicit_flush();$address 127.0.0.1; $port 10005; //创建端口 if( ($sock socket_create(AF_…

通过xss所引起的信息泄露,防不胜防!

话不多说直接上&#xff1a; 信息搜集,通过google语法 site:"*.redacted.com"优化一下: site:"*.redacted.com" -www -blog -mail之后&#xff0c;利用subfinder、assetfinder和masass等被动枚举工具收集与目标相关的子域列表&#xff0c;并将它们保存在…

二叉树的前序、中序、后续、层序遍历(包含递归与非递归)

递归形式 递归形式遍历比较简单&#xff0c;不做详细论述。 前序遍历 void Preorder(treenode* root) //前序 {if (root ! NULL){printf("%c", root->data);Preorder(root->left);Preorder(root->right);}中序遍历 } void Inorder(treenode* root) …

单调递增子序列

单调子序列包含有单调递增子序列和递减子序列&#xff0c;不失一般性&#xff0c;这里只讨论单调递增子序列。首先&#xff0c;从定义上明确我们的问题。给定序列a1, a2, …, an&#xff0c;如果存在满足下列条件的子序列 ai1<ai2<…<aim, (其中i1<i2<…<im)…

51单片机常用功能及相关内容

一、基本概念&#xff1a; 1、引脚 图1.1 这里只介绍常用及主要的引脚。 I/O口引脚&#xff1a;P0、P1、P2、P3 P0口&#xff1a;39脚~32脚&#xff0c;双向8位三态I/O口&#xff0c;每个口可独立控制&#xff0c;但内部无上拉电阻&#xff0c;为高阻态&#xff0c;故不能正常…

No monitoring data is available

No monitoring data is available because monitoring is not enabled for this deployment share...注解&#xff1a;没有监测数据是可用的。报错具体信息如下&#xff1a;Assembly: mscorlib Assembly Version: 2.0.0.0 File Version: 2.0.50727.5420 (Win7SP1.050727-5400…

Unity查安卓Native Crash的方法,定位SO报错函数

需要用到两个工具Il2CppDumper和IDA_Pro&#xff0c;网上可以下到对应的软件 可以看到报错的位置是libil2cpp.so 0000000000AFF820 接下来要做的事情就是找到0000000000AFF820对应的函数是哪个 解包 Il2CppDumper解析so文件和符号表&#xff0c;查看对应的函数表 把apk后缀…

WebApi系列~自主宿主HttpSelfHost的实现

回到目录 宿主一词我们不会陌生&#xff0c;它可以看作是一个基础设施&#xff0c;它为一些服务和功能提供最底层的支持&#xff0c;如你的web应用程序可以运行在iis或者apache上&#xff0c;而这两个东西就是web应用程序的宿主&#xff0c;而今天说的自主宿主SelfHost就是说&a…

linux——进程(创建、终止、等待、替换)

进程的基本操作 概念 程序运行的一个实例&#xff0c;其占有一定的空间。 查询某一进程当前情况 ps aux | grep 进程名终止进程 kill -9 pid&#xff1b; //pid指需要终止的进程pid创建 pid_t fork();该函数有两个返回值&#xff0c;对于子进程其返回的是0&#xf…

第 3-1 课:集合详解(上) + 面试题

先来看看集合的继承关系图,如下图所示: 其中: 外框为虚线的表示接口,边框为实线的表示类;箭头为虚线的表示实现了接口,箭头为实线的表示继承了类。为了方便理解,我隐藏了一些与本文内容无关的信息,隐藏的这些内容会在后面的章节中进行详细地介绍。 从图中可以看出,集…

CCNA 学习笔记(四)--路由协议(RIP)

现在我们先复习下&#xff0c;什么是路由&#xff1f;答&#xff1a;当路由器&#xff08;或者其它三层设备&#xff09;收到一个IP数据包时&#xff0c;会查看数据包的IP头部中的目的IP地址&#xff0c;并在路由表中进行查找&#xff0c;在匹配到最优路由后&#xff0c;将数据…