集合及数据结构第八节(下)———— 队列(Queue)、队列的模拟实现和练习

news2025/4/8 23:19:09

系列文章目录

集合及数据结构第八节(下)———— 队列(Queue)、队列的模拟实现和练习

队列(Queue)、队列的模拟实现和练习

  1. 队列的概念
  2. 队列的使用
  3. 队列模拟实现
  4. 循环队列
  5. 双端队列
  6. 练习题

文章目录

  • 系列文章目录
    • 集合及数据结构第八节(下)———— 队列(Queue)、队列的模拟实现和练习
  • 一、队列(Queue)
    • 1.队列的概念
    • 2.队列的使用( * *
    • 3.队列模拟实现( * * *
      • 定义接口
      • 接口的实现
        • 入队列
        • 出队列
        • 获取队头元素
        • 获取队列中有效元素个数
        • 获取队头元素
    • 4.循环队列
      • 如何区分空与满
      • 数组下标循环的小技巧
      • 设计循环队列
    • 5.双端队列 (Deque)
  • 二、练习题
    • 1.用队列实现栈
      • 思路:
      • 代码实现:
    • 2.用栈实现队列
      • 思路:
      • 代码实现:


一、队列(Queue)

1.队列的概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First
In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
在这里插入图片描述

2.队列的使用( * *

在这里插入图片描述
在这里插入图片描述
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口

import java.util.LinkedList;
import java.util.Queue;
    public static void main(String[] args) {
        Queue<Integer> q = new LinkedList<>();
        q.offer(1);
        q.offer(2);
        q.offer(3);
        q.offer(4);
        q.offer(5); // 从队尾入队列
        System.out.println(q.size());
        System.out.println(q.peek()); // 获取队头元素
        q.poll();
        System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
        if(q.isEmpty()){
            System.out.println("队列空");
        }else{
            System.out.println(q.size());
        }
    }

在这里插入图片描述

3.队列模拟实现( * * *

队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过前面线性表的学习了解到常见的空间类型有
两种:顺序结构链式结构而队列的实现使用链式结构更好
在这里插入图片描述

定义接口

public interface Iquque {
    boolean offer(int val);//入队列
    int poll();//出队列

    int peek();//获取队头元素
    int size();//获取队列中有效元素个数
    boolean empty();//检测队列是否为空
}```
### <font color = "golden yellow'"> 定义队列的内部类</font>

```java
    static class ListNode {//将节点定义成内部类
        public int val;//数据域
        public ListNode next;//后驱节点(引用类型)
        public  ListNode prev;//前驱节点

        public ListNode(int val) {
            this.val = val;
            //没有初始化next默认为null(因为next是引用数据类型)
        }
    }

    public ListNode head;//头节点
    public ListNode last;//尾节点
    public int usedsize;//有效节点个数

接口的实现

入队列

思路:

第一次插入时head和last都指向这个节点
在这里插入图片描述

在这里插入图片描述

代码实现:

    public boolean offer(int val) {//入队列
        ListNode node = new ListNode(val);//创建一个node节点用来存放插入的数据
        if(head == null){//插入第一个数据
            this.head = node;
            this.last = node;
        }else {//有其他节点,进行尾插
            last.next = node;//先绑定后面更安全
            node.prev = this.last;
            this.last = node;
        }
        usedsize++;
        return true;//插入成功
    }
出队列
public class QueueEmptyWrong extends RuntimeException{
    public QueueEmptyWrong(String message){
        super(message);
    }
}
    public int poll() {//出队列
        if (head == null){//队列为空,抛出异常
            throw new QueueEmptyWrong("队列是空的,不能进行出队列");
        }
        int retVal = head.val;//用来存放这个节点的val值
        if (head.next == null){//只有一个节点
            head = null;
            last = null;
            return retVal;//返回这个节点的val值
        }
        //不止一个节点时
        head = head.next;//头节点不在指向第一个节点(指向第二个节点)
        head.prev = null;//第二个节点的前驱置为null(第一个节点出队列前)
        usedsize--;
        return retVal;//返回这个节点的val值
    }
获取队头元素
    public int peek() {//获取队头元素
        if (head == null){//队列为空,抛出异常
            throw new QueueEmptyWrong("队列是空的,不能进行出队列");
        }
        return head.val;//返回头节点的val值
    }
获取队列中有效元素个数
    public int size() {//获取队列中有效元素个数
        return usedsize;
    }
获取队头元素
    public boolean empty() {//检测队列是否为空
        return head == null;
    }

4.循环队列

实际中有时还会使用一种队列叫循环队列。如操作系统中讲解生产者消费者模型时可以就会使用循环队列。环形队列通常使用数组实现。
在这里插入图片描述

如何区分空与满

  1. 通过添加usedsize 属性记录
  2. 浪费一个空间表示满

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

  1. 使用标记

数组下标循环的小技巧

  1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length
    在这里插入图片描述
  2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length
    在这里插入图片描述

设计循环队列

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,能使用这些空间去存储新的值。

队列的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。
class MyCircularQueue {

    public int[] elem;

    public int front;//队头
    public int rear; //队尾

    public MyCircularQueue(int k) {//构造器,设置队列长度为 k 。
        elem = new int[k + 1];//实际能存放的数据只有k个
    }
    
    public boolean enQueue(int value) {//向循环队列插入一个元素。如果成功插入则返回真
        if (isFull()){//如果满了就不能放了
            return false;
        }

        //没有满就要插入元素
        elem[rear] = value;
        rear = (rear + 1) % elem.length;
        return true;
    }
    
    public boolean deQueue() {//从循环队列中删除一个元素。如果成功删除则返回真
        if (isEmpty()){//如果队列是空的就不能删除
            return false;
        }
        front = (front + 1) % elem.length;
        return true;
    }
    
    public int Front() {//从队首获取元素。如果队列为空,返回 -1
        if (isEmpty()){//如果队列是空的返回-1
            return -1;
        }
        return elem[front];
    }
    
    public int Rear() {//获取队尾元素。如果队列为空,返回 -1
        if (isEmpty()){//如果队列是空的返回-1
            return -1;
        }
        int index = (rear == 0) ? elem.length - 1 : rear -1;//当队尾指向0下标的位置时
        return elem[index];                                     // index队尾对应 下标就是elem.length - 1,否则直接就是rear - 1

    }
    
    public boolean isEmpty() {//检查循环队列是否为空。
        return front == rear;//当front 和 rear相等时为空
    }
    
    public boolean isFull() {//检查循环队列是否已满
        return (rear + 1) % elem.length == front;
    }
}

5.双端队列 (Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队

在这里插入图片描述
Deque是一个接口,使用时必须创建LinkedList的对象。
在这里插入图片描述
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

二、练习题

1.用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty
这些操作。 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 ,只要是标准的队列操作即可。

思路:

  1. 创建两个队列。入栈的时候,哪个队列不为空放到哪个队列里(两个都是空的指定放到第一个里面)
  2. 出栈的时候哪个不为空出哪个(出size - 1 个元素放到第二个、队列里,再出第一个队列的元素)
  3. 当两个队列同时是空的,模拟的栈就是空的

代码实现:

class MyStack {

    private Queue<Integer> queue1;
    private Queue<Integer> queue2;
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {//将元素 x 压入栈顶
        if (!queue1.isEmpty()){//第一个队列不为空,放进第一个队列
            queue1.offer(x);
        } else if (!queue2.isEmpty()) {//第二个队列不为空,放进第二个队列
            queue2.offer(x);
        }else {//两个都是空的指定放到第一个里面
            queue1.offer(x);
        }
    }
    
    public int pop() {// 移除并返回栈顶元素
        if (empty()) {//当两个队列都是是空的,返回-(错误)
           return -1;
        }
        if (!queue1.isEmpty()){//第一个队列不为空,出size - 1个元素到queue2中
            int size = queue1.size();
            for (int i = 0; i < size - 1; i++) {
                int x = queue1.poll();//弹出第size - 1个元素放到x里面
                queue2.offer(x);//存到queue2里面
            }
            return queue1.poll();//再出第一个队列的元素
        } else  {//第二个队列不为空,出size - 1个元素到queue1中
            int size = queue2.size();
            for (int i = 0; i < size - 1; i++) {
                int x = queue2.poll();//弹出第size - 1个元素放到x里面
                queue1.offer(x);//存到queue1里面
            }
            return queue2.poll();//再出第二个队列的元素
        }
    }
    
    public int top() {//返回栈顶元素。
        if (empty()) {//当两个队列都是是空的,返回-(错误)
            return -1;
        }
        if (!queue1.isEmpty()){//第一个队列不为空,出size 个元素到queue2中
            int size = queue1.size();
            int x = -1;
            for (int i = 0; i < size ; i++) {
                 x = queue1.poll();//弹出第size 个元素放到x里面
                queue2.offer(x);//存到queue2里面
            }
            return x;//返回队列最后的元素(栈顶的元素)
        } else  {//第二个队列不为空,出size 个元素到queue1中
            int size = queue2.size();
            int x = -1;
            for (int i = 0; i < size ; i++) {
                 x = queue2.poll();//弹出第size 个元素放到x里面
                queue1.offer(x);//存到queue1里面
            }
            return x;//返回队列最后的元素(栈顶的元素)
        }
    }
    
    public boolean empty() {//如果栈是空的,返回 true ;否则,返回 false
        return queue1.isEmpty() && queue2.isEmpty();//当两个队列同时是空的,模拟的栈就是空的
    }
}

2.用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例 1:

输入:
[“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

思路:

  • 入队的时候,都放到第一个栈里。
    在这里插入图片描述
  • 出队的时候,把第一个栈的元素移到第二个栈中,然后再出第二个栈中的元素。
  • List item

在这里插入图片描述

代码实现:

package queuedemo;

import java.util.Stack;

class MyQueue {

    private Stack<Integer> s1;//第一个栈
    private Stack<Integer> s2;//第二个栈
    public MyQueue() {
        s1 = new Stack<>();//实例化s1
        s2 = new Stack<>();//实例化s2
    }
    
    public void push(int x) {// 将元素 x 推到队列的末尾
        s1.push(x);
    }
    
    public int pop() {//从队列的开头移除并返回元素
        if (empty()){//如果队列为空,返回-1
            return -1;
        }
        if (s2.empty()){//当s2为空的时候把s1的元素移到s2里(当s2不为空的时候先把s2出完)
            while (!s1.empty()){
                s2.push(s1.pop());
            }
        }
        return s2.pop();
    }
    
    public int peek() {//返回队列开头的元素
        if (empty()){//如果队列为空,返回-1
            return -1;
        }
        if (s2.empty()){//当s2为空的时候把s1的元素移到s2里
            while (!s1.empty()){
                s2.push(s1.pop());
            }
        }
        return s2.peek();
    }
    
    public boolean empty() {//如果队列为空,返回 true ;否则,返回 false
        return s1.empty() && s2.empty();
    }
}

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

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

相关文章

Chainlit接入DifyAI知识库接口快速实现自定义用户聊天界面

前言 由于dify只提供了一个分享用的网页应用&#xff0c;网页访问地址没法自定义&#xff0c;虽然可以接入NextWeb/ChatGPT web/open webui等开源应用。但是如果我们想直接给客户应用&#xff0c;还需要客户去设置配置&#xff0c;里面还有很多我们不想展示给客户的东西怎么办…

【C语言】文件操作 (详细!!)

1、为什么使用文件 使用文件的原因&#xff1a;使用文件主要是为了在程序的执行过程中保存、读取和交换数据。文件提供了一种持久化存储数据的方式&#xff0c;使得程序在关闭后&#xff0c;数据不会丢失&#xff0c;可以被其他程序或后续的程序执行周期重新读取和处理。 1.0 什…

实验2-1-3 输出三角形

本题要求编写程序&#xff0c;输出指定的由“*”组成的三角图案。 **输入格式&#xff1a; 本题无输入**输出格式&#xff1a; 按照下列格式输出由“*”组成的三角图案。 **** *** ** *程序: #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int i…

leetcode 49 字母异位分词

正文 基础解法 首先&#xff0c;我们创建一个字典对象&#xff0c;然后遍历整个字符串列表&#xff0c;并且使用 sorted() 函数对字符串列表进行排序&#xff0c;所有的异位分词经过排序后它们的组成和顺序会趋于一致。但是需要注意的是 sorted 对字符串进行排序后会变成一个由…

基于element-ui 日期选择器el-date-picker, 即对日期做区间限制

需求&#xff1a; 有时候需求会让我们对日期选择器做限制&#xff0c;即控制最多可跨越多少个月份&#xff0c;其中涉及到不同年份该如何计算。 HTML&#xff1a; <el-date-pickerv-model"timePeriod"type"monthrange"value-format"yyyyMM"…

Linux系统之部署俄罗斯方块网页小游戏(三)

Linux系统之部署俄罗斯方块网页小游戏(三) 一、小游戏介绍1.1 小游戏简介1.2 项目预览二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、检查本地环境3.1 检查系统版本3.2 检查系统内核版本3.3 检查软件源四、安装Apache24.1 安装Apache2软件4.2 启动apache2服务4.3 查看…

【CANoe使用大全】——cdd导入CANoe流程详解

&#x1f64b;‍♂️【CANoe使用大全】系列&#x1f481;‍♂️点击跳转 文章目录 1.1.CDD导入1.1 CDD文件导入流程 2. CDD文件导后配置2.1.协议配置2.2.寻址方式配置2.3.0x27 解密DLL导入2.4.诊断ID配置 3.导入效果4.CDD操作台使用4.1.指令发送 5.Fault Memory5.1 0x19 045.2…

解释图像的边缘检测算法中的Canny算法

Canny 算法是图像处理领域中一种经典的边缘检测方法&#xff0c;由 John F. Canny 在 1986 年提出。Canny 算法以其高效、可靠的边缘检测效果在图像处理和计算机视觉领域广泛应用。它具有良好的噪声抑制能力、精确的边缘定位能力以及单像素宽度的边缘输出特性。 Canny 边缘检测…

TIM输出比较之PWM驱动LED呼吸灯应用案例

文章目录 前言一、应用案例演示二、电路接线图三、应用案例代码四、应用案例分析4.1 基本思路4.2 相关库函数介绍4.3 初始化PWM模块4.3.1 RCC开启时钟4.3.2 配置时基单元4.3.3 配置输出比较单元4.3.4 配置GPIO4.3.5 运行控制 4.4 PWM输出模块4.5 主程序 前言 提示&#xff1a;…

无人机培训与装配维修技术详解

一、无人机基础理论 无人机&#xff0c;即无人驾驶航空器&#xff0c;凭借其灵活性、高效性和广泛应用性&#xff0c;已成为现代科技领域的热点之一。在学习无人机培训与装配维修技术之前&#xff0c;掌握无人机的基础理论是必不可少的。这包括但不限于&#xff1a; 1. 无人机…

Alpaca 汉化版 v2.9.3 — 免费 PS 智能 AI 插件

Alpaca是一款免费的PS智能AI插件&#xff0c;包含了6大AI功能&#xff0c;包括提示词生图、图像转绘画风格、生成式填充、文本转图像、计算图像模型、提高图像分辨率。汉化版本安装简单&#xff0c;只需解压到PhotoShop安装目录\Plug-ins文件夹即可。安装启动PhotoShop - 增效工…

基于Springboot和BS架构的宠物健康咨询系统pf

TOC springboot509基于Springboot和BS架构的宠物健康咨询系统pf 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#x…

前端本地代理配置方式

Whistle 介绍 Whistle 是一个基于 Node.js 的跨平台 Web 调试工具。允许捕获、查看和修改 HTTP/HTTPS 网络请求。通过使用 Whistle&#xff0c;可以轻松地进行接口代理、抓包、模拟数据、修改请求和响应等操作&#xff0c;以便在前端开发中调试网络请求。 Proxy SwitchyOmega…

记录一个变量溢出的bug

文章目录 如题 如题 count2变量溢出了&#xff08;超过了255&#xff09;&#xff0c;结果导致busOff_16bitRecordHILTime变量莫名其妙被清0

c++题目_背包问题(可任意分割) 贪心算法

题目描述 有一个背包&#xff0c;背包容量是mm。有nn个物品&#xff0c;每个物品都有自己的重量wiw​i​​和价值viv​i​​&#xff0c;物品可以分割成任意大小。 要求尽可能让装入背包中的物品总价值最大&#xff0c;但不能超过总容量。 输入 第一行输入两个正整数 mm 和 n…

【C++】提高 -- 类模板

目录 一、类模板的作用 二、类模板的语法 三、类模板的例子 四、类模板和函数模板的区别 五、类模板中成员函数创建时机 六、类模板对象做函数参数 七、类模板与继承 八、类模板成员函数类外实现 九、类模板分文件编写 十、类模板与友元 十一、类模板案例 一、类模板…

日撸Java三百行(day31:整数矩阵及其运算)

目录 前言 一、基本属性与方法 二、getter与setter方法 三、矩阵相加与矩阵相乘方法 1.矩阵相加 2.矩阵相乘 四、数据测试 五、完整的程序代码 总结 前言 从今天开始&#xff0c;我们就要踏上图论的学习之路了。第一天&#xff0c;我们先简单热个身&#xff0c;构造一…

手持气象站:便携式、高精度设备

在科技日新月异的今天&#xff0c;气象观测技术正以前所未有的速度发展&#xff0c;从传统的地面观测站、高空探测到卫星遥感&#xff0c;每一步都极大地拓宽了我们对天气的认知边界。而在这股科技浪潮中&#xff0c;手持气象站作为一种便携式、高精度的气象监测设备&#xff0…

Ps:首选项 - 文件处理

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“文件处理” File Handling选项卡允许用户精确控制 Photoshop 的文件保存行为和兼容性选项。这些设置非常重要&#xff0c;尤其在处理大文件或与其他软件协作时&#xff0c;可…

用Zipkin在分布式系统追踪收集和查看时间数据

Zipkin是一个开源的分布式追踪系统&#xff0c;它帮助收集、存储和展示实时的数据&#xff0c;以便于定位微服务架构中的延迟问题。以下是Zipkin的核心组件和工作流程的介绍&#xff0c;以及如何在Java中使用Spring Cloud Sleuth与Zipkin集成的案例。 Zipkin的核心组件&#x…