队列及其经典面试题

article/2025/9/18 15:12:14

前言

上一篇讲了栈和栈的经典面试题,链接如下:

栈与栈的经典面试题

其实栈和队列是一码事,都是对只能再线性表的一端进行插入和删除。

因此,其实栈和队列可以互相转换!

一、队列的特点

先进先出的数据结构,元素从“队尾”添加到队列中,元素从“队首”出队列 (FIFO)

在这里插入图片描述


二、队列的实现

1.基于链表实现队列

现实生活中,有各式各样的“排队”操作。

同样的,队列也有基于数组实现的队列和基于链表实现的队列。

由于出队操作只能在队列的头部进行,若采用数组的方案,每次出队一个元素就得搬移剩下的所有元素向前移动一个单位。

此时采用链表的方案更加适合队列的结构。

2.核心操作

  • E poll() : 出队

  • offer(E e) : 入队


三、双端队列

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。

那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

Deque -> Queue的子接口:

在这里插入图片描述

小技巧:

  • 大家以后无论使用的是栈还是队列,统一使用双端队列接口!
  • 不推荐使用Stack这个类,这个类已经是时代的弃子,效率很低,都是同步操作。
  • 双端队列的一个常用子类就是LinkedList。

在这里插入图片描述
在这里插入图片描述


四、循环队列

1.应用场景

  • 操作系统的生产消费者模型
  • MySQL数据库的InnoDB存储引擎中的redo日志。

2.实现

  1. 基本都是使用长度固定的数组实现,数组在实现队列时,若从数组头部删除元素需要频繁的移动后面的元素,带来的效率比较低。

  2. 出队和入队操作,使用两个引用,一个head,一个tail,添加元素在数组尾部添加,删除元素只需要移动head引用指向的地址即可(逻辑删除)。

循环队列就是使用长度固定的数组来实现,数组头部就是队首(head),数组的尾部就时队尾(tail)。数组[head…tail]是循环队列的有效元素。

例如:

在这里插入图片描述

  • head永远指向循环队列的第一个元素
  • tail永远指向循环队列有效元素的后一个位置

循环队列在删除元素时,不需要进行数据的搬移,当有新的元素在添加时就会覆盖掉之前的元素。

所谓的循环队列指的是当head或者tail引用走到数组末尾时,下一次再继续向后移动,其实是返回数组的头部继续操作。

3.判空和判满

由于tail指向的是有效数组后一个位置,所以当tail走到如图所示的head == tail 情况时:

在这里插入图片描述

此时我们没法区分当前循环队列到底是空还是满!!!

所以在循环队列中,需要浪费一个空间来判断队列是否已满,如下图所示:

在这里插入图片描述

结论:

  1. 此时当 (tail+1)%n == head 时,认为此时循环队列已满。
  2. head和tail的移动不能简单的+1,而要使用取模操作,取数组长度。
  3. 当head == tail 时 ,认为此时循环队列为空。

4.最后一个元素的索引

  • 除了tail这个引用指向0这个位置以外,其他情况的最后一个索引 = tail - 1
  • 当 tail = 0 时,最后一个元素就在数组的末尾,索引 = data.length - 1

在这里插入图片描述

代码如下:

int lastIndex = tail == 0 ? data.length -1 : tail - 1

五、队列的常见问题

1.用队列实现栈

链接如下:225.用队列实现栈

在这里插入图片描述


解题思路:

思路1:(双队列思路)

这个问题的本质和双栈实现最小栈是相同的思路,一定要保证其中一个队列就是进行实际元素存储的,另一个队列就是作为辅助操作。

q1永远是存储元素的队列,新元素添加到q2中,将此时q1中的所有元素出队再入队q2恰好就能实现添加顺序和出队顺序相反的操作

  1. 新元素永远入q2
  2. 将老元素q1依次出队再入q2 (这样就交换了元素的先后顺序)
  3. q1和q2交换引用

在这里插入图片描述

  • 其中一个队列q1永远都是存储元素的队列,栈的pop就是s1的poll,栈的peek就是s1的peek,栈的push就是s1的offer。 (所以整个题的核心操作就是push()

  • 以保证s1和栈的操作一一对应。

  • 另外一个队列就是作为辅助操作。

代码如下:

class MyStack {Deque<Integer> temp;Deque<Integer> q1;Deque<Integer> q2;public MyStack() {q1 = new LinkedList<>();q2 = new LinkedList<>();}public void push(int x) {q2.offer(x);while(!q1.isEmpty()){q2.offer(q1.poll());}temp = q1;q1 = q2 ;q2 = temp;}public int pop() {return q1.poll();}public int top() {return q1.peek();}public boolean empty() {return q1.isEmpty();}
}

思路2:(单队列思路)

在这里插入图片描述

  1. 先记录下当前队列中的元素个数n
  2. 将新元素直接入队
  3. 为了让新元素变成队首元素,连续出队n次(新元素的之前的所有元素都出队列,此时新元素变成了队首元素),再依次入队n次即可。

代码如下:

class MyStack {private Deque<Integer> queque;private int length=0;//1.先记录一下栈此时的元素个数public MyStack() {queque=new LinkedList<>();}public void push(int x) {queque.offer(x);//2.新元素直接入队//3.把新元素之前的元素挨个出队再入队,此时最新的元素就是队头元素for (int i=0;i<length;i++){queque.offer(queque.poll());}length++;}public int pop() {length--;return queque.poll();}public int top() {return queque.peek();}public boolean empty() {return queque.isEmpty();}
}

2.用栈实现队列

链接如下:用栈实现队列

在这里插入图片描述

解题思路:

思路1:(入队 - 时间复杂度为O(n),出队 - O(1))

定义s1永远是保存元素的栈

  1. 先将s1中的现有元素依次弹出放入s2
  2. 将新元素入s1 => 此时这个新元素就变成了s1的栈底(队尾元素)
  3. 将s2中的元素再依次弹回来(先进先出)

在这里插入图片描述

代码如下:

class MyQueue {Deque<Integer> s1;Deque<Integer> s2;public MyQueue() {s1 = new LinkedList<>();s2 = new LinkedList<>();}public void push(int x) {while(!s1.isEmpty()){s2.push(s1.pop());}s1.push(x);while(!s2.isEmpty()){s1.push(s2.pop());}}public int pop() {return s1.pop();}public int peek() {return s1.peek();}public boolean empty() {return s1.isEmpty();}
}

思路2:(入队 - 时间复杂度为O(1),出队-摊还复杂度O(1))

  1. push是正常push的,核心操作在pop里面
  2. push进s1的元素依次出栈再入s2栈的时候,出入顺序就会颠倒
  3. 根据上述特性,把s2的栈顶当作队首就行了,因为队列都是队首出队的。
  4. 每次pop先判断当前s2是否为空,若为空,则把s1中的元素全部出栈并且压进s2里面,此时s2的栈顶就是队首元素(先进先出)。若不为空,证明上一轮push进来的元素还没pop完,继续pop当前s2的栈顶就行。
class MyQueue {Deque<Integer> s1 ;Deque<Integer> s2 ;public MyQueue() {s1 = new LinkedList<>();s2 = new LinkedList<>();}public void push(int x) {s1.push(x);}public int pop() {if(s2.isEmpty()){while(!s1.isEmpty()){s2.push(s1.pop());}}return s2.pop();}public int peek() {if(s2.isEmpty()){while(!s1.isEmpty()){s2.push(s1.pop());}}return s2.peek();}public boolean empty() {return s1.isEmpty() && s2.isEmpty();}
}

总结

以上就是队列及其经典面试题的全部内容了,有帮助的话麻烦各位给个三连~~感谢!!!


http://chatgpt.dhexx.cn/article/5iVyPYly.shtml

相关文章

消息队列面试经典十连问

前言 金三银四即将来临&#xff0c;整理了十道十分经典的消息队列面试题&#xff0c;看完肯定对面试有帮助的&#xff0c;大家一起加油哈~ 什么是消息队列消息队列的应用场景消息队列如何解决消息丢失问题消息队列如何保证消息的顺序性。消息有可能发生重复消费吗&#xff1f…

一些常见的消息队列面试题整理

你们公司生产环境用的是什么消息中间件&#xff1f; RabbitMQ、ActiveMQ、RocketMQ、Kafka优缺点与应用场景 为什么在你们系统架构中要引入消息中间件&#xff1f; 系统解耦、异步调用、流量削峰 说说系统架构引入消息中间件有什么缺点&#xff1f; 系统可用性降低(MQ挂了)、…

Java笔试面试-消息队列面试题总结

1.消息队列的应用场景有哪些&#xff1f; 答&#xff1a;消息队列的应用场景如下。 应用解耦&#xff0c;比如&#xff0c;用户下单后&#xff0c;订单系统需要通知库存系统&#xff0c;假如库存系统无法访问&#xff0c;则订单减库存将失败&#xff0c;从而导致订单失败。订…

Java面试题消息队列

消息队列的架构图: 生产者发送消息的流程: -- 消息的发送者(Producer)和RabbitMQ建立连接,获取通道. -- 生产者发送消息到指定虚拟机中的交换机(exchange), -- 交换机通过routhingKey来获取对应的队列. 消费者消费消息的流程: -- 消息的消费者(Consummer)和RabbitMQ建…

消息队列常见面试题

文章目录 2. 消息队列2.1 MQ有什么用&#xff1f;2.2 说一说生产者与消费者模式2.3 消息队列如何保证顺序消费&#xff1f;2.4 消息队列如何保证消息不丢&#xff1f;2.5 消息队列如何保证不重复消费&#xff1f;2.6 MQ处理消息失败了怎么办&#xff1f;2.7 请介绍消息队列推和…

MQ消息队列面试题

MQ消息队列面试题 什么是消息队列 消息队列&#xff0c;就是指保存消息的一个容器。类似于数据库、缓存等&#xff0c;用来保存数据的。 消息队列&#xff0c;就是一个使用队列来通信的组件 为什么需要消息队列&#xff0c;消息队列的应用场景 提供系统性能首先考虑的是数据库…

常见消息队列面试题

常见消息队列面试题 1.为什么要用消息队列?(消息队列的应用场景&#xff1f;) 消息队列的本质&#xff1f; 消息队列是一种“先进先出”的数据结构&#xff0c;一般用其作为数据的传递 常见的应用场景&#xff1a;解耦&#xff0c;异步以及削峰 解耦: 场景&#xff1a;双11是…

消息队列 面试题

1、面试题 为什么使用消息队列啊&#xff1f;消息队列有什么优点和缺点啊&#xff1f;kafka、activemq、rabbitmq、rocketmq都有什么区别以及适合哪些场景&#xff1f; 2、面试官心理分析 其实面试官主要是想看看&#xff1a; &#xff08;1&#xff09;第一&#xff0c;你…

消息队列面试题及答案

1、为什么使用消息队列&#xff1f; 消息队列使用的场景和中间件有很多&#xff0c;但解决的核心问题主要是&#xff1a;异步、解耦、消峰填谷。 2、消息队列的优缺点 异步、解耦、消峰填谷这是消息队列最大的优点&#xff0c;除了这些消息队列还可以会解决一些我们特殊业务…

精心整理14道高频消息队列场景面试题(建议收藏)

消息队列是大型系统中常用的一个组件&#xff0c;也是项目的亮点和面试的重点。常见的的分布式系统中有RabbitMQ、ActiveMQ、RocketMQ等&#xff0c;而在大数据项目中比较常用的是Kafka。今天我整理了几道在面试中常见的消息队列面试题&#xff0c;供大家学习参考。 1、消息队列…

消息队列面试题(2022最新整理)

为什么要使用消息队列&#xff1f; 总结一下&#xff0c;主要三点原因&#xff1a;解耦、异步、削峰。 1、解耦。比如&#xff0c;用户下单后&#xff0c;订单系统需要通知库存系统&#xff0c;假如库存系统无法访问&#xff0c;则订单减库存将失败&#xff0c;从而导致订单操…

浙大Python 第2章-9 比较大小 (10 分)

专题博客链接 [题解]浙大Python PTA课后习题博客记录(Python) 原题题目 代码实现&#xff08;输出法1&#xff09; a,b,c map(int,input().split()) temp [a,b,c] list.sort(temp) print("%d->%d->%d" % (temp[0],temp[1],temp[2]))代码实现&#xff08;输…

Anagrams by Stack | Python 实现

目录 1.题面 2.注意事项&#xff1a; 0.OJ平台 1.无限流输入 、EOF输入流 2.返回中的空格 3.AC代码 1.题面 Anagrams by Stack Time Limit: 2000 msMemory Limit: 65536 KB How can anagrams result from sequences of stack operations? There are two sequences of sta…

用Python统计字符串个数

1.题目 输入一行字符&#xff0c;分别统计出其中英文字母、空格、数字和其它字符的个数。 2.程序分析 利用while语句,条件为输入的字符不为’\n’. from pip._vendor.distlib.compat import raw_inputs raw_input(请输入字符串:\n) letters 0 space 0 digit 0 others …

PTA-MOOC《Python程序设计浙江大学》拼题题目集第一章编程题

7-1 从键盘输入两个数&#xff0c;求它们的和并输出 (30分) ****本题目要求读入2个整数A和B&#xff0c;然后输出它们的和。 输入格式: 在一行中给出一个被加数 在另一行中给出一个加数 输出格式: 在一行中输出和值。 输入样例: 在这里给出一组输入。例如&#xff1a; 输出样…

【Python】找出不是公共的元素

目录 找出不是公共的元素 代码思路仅供参考&#xff0c;欢迎大家批评指正&#xff01; 找出不是公共的元素 给定两行输入&#xff0c;每行代表一组元素。每行的元素间用空格分开。求两组中非公共的元素。 # By jurio. a[i for i in input().split()] b[i for i in input().spl…

【PTA】【Python】【拼题A 2022 跨年挑战赛】小孩子才做选择,大人全都要

阿汪面前有两只盲盒&#xff0c;每只盒子打开都有两种可能&#xff1a;或者装了 X 克狗粮&#xff0c;或者是一只容量为 Y 克的狗粮储蓄盒。如果是狗粮&#xff0c;阿汪可以快乐地吃掉&#xff1b;如果是空储蓄盒&#xff0c;那就倒霉了&#xff0c;阿汪必须想办法找到狗粮把这…

【PTA】【Python】【拼题A 2022 跨年挑战赛】太神奇了

“告诉大家一个神奇的消息&#xff0c;太神奇了&#xff1a;明年全世界所有的人都同岁&#xff0c;全部都等于2022。明年的日子很特别&#xff0c;大概每1000年才会有一次。明年你的周岁年龄你的出生年&#xff0c;每个人都是2022年。例如&#xff1a;你明年57加上1965年生的&a…

LeetCode100题之—3、一汉明距离(python)

汉明距离 题目描述解题思路自己写的答案&#xff0c;不便捷1&#xff09;利用循环2&#xff09;利用右移别人给的高效答案 题目描述 解题思路 自己写的答案&#xff0c;不便捷 1&#xff09;利用循环 分为两个步骤 主要求出两个数二进制形式上下对应时不相同的个数 1&#x…

PTA-MOOC《Python程序设计浙江大学》拼题A题目集第二章编程题

7-1 计算 111213…m (30分) 输入一个正整数m(20<m<100)&#xff0c;计算 111213…m 的值。 输入格式: 在一行输入一个正整数m。 输出格式: 在一行中按照格式“sum S”输出对应的和S. 输入样例: 在这里给出一组输入。例如&#xff1a; 输出样例: 在这里给出相应的输…