【数据结构初阶】第六节.队列的实现

news2024/10/6 8:21:46

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

前言

一、队列的初步认识

 二、Java中队列的使用

三、队列的模拟实现

四、力扣刷题演练

4.1 设计循环队列

4.2 用栈实现队列

4.3 最小栈  

总结


前言

今天我们将介绍有关队列的有关内容;我们将对队列的一些初步认识;以及常见队列的使用;


提示:以下是本篇文章正文内容,下面案例可供参考

一、队列的初步认识

队列,和栈一样,也是一种对数据的"存"和"取"有严格要求的线性储存结构。

与栈结构不同的是,队列的两端都"开口",要求数据只能从一端进,从另一端出;

如图所示:

通常,称进数据的一端为 "队尾",出数据的一端为 "队头",数据元素进队列的过程称为 "入队",出队列的过程称为 "出队"。

比如上图中的元素3就是队尾,元素1就是队头。从图中我们也可以看出,元素1是最先进入队列中的,同样他也是最先出队的,所以说队列是一种先进先出的结构

 

 二、Java中队列的使用

在Java中,队列Queue是个接口,底层是用双向链表实现的。

他主要的方法有一下这几个:

注意:Queue是个接口,我们不能直接对Queue进行实例化,但我们可以用Queue接口实例化LinkedList的对象,因为LinkedList实现了Queue接口。 

使用实例:

三、队列的模拟实现

// 用单链表实现的队列,入队和出队的时间复杂度都是O(1)
public class MyQueue2 {
    class ListNode{
        public int val;
        public ListNode next;
        ListNode(int val) {
            this.val = val;
        }
    }
    ListNode head;
    ListNode last;
    // 入队,从尾入,从头出
    public void offer(int x) {
        ListNode node = new ListNode(x);
        if (empty()) { // 如果此时队列为空,新插入的结点就是头结点和尾巴结点
            head = node;
            last = node;
        }
        else {
            last.next = node;
        }
        last = node;
    }
    // 出队
    public int poll() {
        if (empty()) {
            throw new NullPointerException("当前队列为空,你的操作不合法!");
        }
        if (head == last) head = last = null;
        int tmp = head.val; // 先保留一下头节点的值,然后再更改指向
        head = head.next;
        return tmp;
    }
    // 只是获取将要出队的元素的值,不删除元素
    public int peek() {
        return head.val;
    }
    // 判断当前队列是否为空
    public boolean empty() {
        if (head == null) {
            return true;
        }
        return false;
    }
}

思路:  
1.用单链表实现的队列,为了入队和出队的时间复杂度都是O(1),
2.我们还在单链表中设置了一个对尾结点的引用last,同时还需要保证我们都是尾插入队,头删出队
 为什么呢?因为单链表只有后驱,没有前驱(即只知道后一个是谁,但不知道前一个是谁)
1.如果我们要尾删出队——就必须找到该结点的前一个是谁,就需要遍历链表O(N)时间复杂度
2.而如果头删,我们直接更改当前头结点的指向就好了,时间复杂度自然是O(1)
那为啥要尾插入队呢?我们如果从尾巴插入,是不是只要将当前的的尾巴结点指向新插入的结点就行了,
此时新插入的结点就变成了新的尾巴结点,时间复杂度也是O(1)

测试:

 

四、力扣刷题演练

4.1 设计循环队列

题目链接:力扣

分析 :

我们刚才是用链表实现的队列,而这道题目要求我们用数组这种线性储存结构来实现队列的各种操作,所以我们就需要改变一下设计思想

数组是线性储存元素,那么我们的新增和删除该怎么操作呢?新增和删除时数组的下标是怎样变化的呢?题目中说的循序是怎样进行的呢?

思路:

在这个循环队列中,我们用front来表示队头的数组下标、rear表示队尾的数组下标。为了实现循环我们发现,所谓的头和尾其实是在不断变化着的,和链表一样,我们还是从尾巴入队,从头部出队;

当front == rear是表示当前队列为空,当一个元素入队后,表示队尾的数组下标rear就加一;出队后表示队头的数组下标front就加一,但问题了来了——如何判断当前队列是否满了呢?

有三种方法

  1. 设置一个标记flag;
  2. 每增加或减少一个元素后,用计数器usedSize记录当前队列中元素的个数。当usedSize == 数组的长度时说明满了;
  3. 空一格,当(rear + 1) % 数组的长度 == front   的时候说明数组满了;

说到这里,你可能对取模有点疑惑,为什么要取模呢???

📝数组的下标是可以一直增加的——很有可能会超过数组的长度,因为随着数组的增加和删除front和rear都是不断往前走的;

📝比如数组中能容纳的元素总个数是8,即最大下标是7。当我们新增了7个元素,rear变成了 6,然后我们又删除了4个元素,即front变成了3;

📝此时数组中只剩下了3个元素——也就是还能增加5个元素,即rear还能加5,但rear + 5不就等于11了吗?超过最大下标了呀!但其实此时的增加的确是合法的呀!所以要对数组下标及时的取模;

💖代码实现:

class MyCircularQueue {
    int[] elem; // 我们这个队列是用数组实现的
    int front;  // 表示队头的数组下标
    int rear;   // 表示队尾的数组下标
/**
* 构造方法
* @param 数组长度是k
*/
public MyCircularQueue(int k) {
    elem = new int[k + 1]; // 为啥要k + 1,因为我们在判断队列是否满的时候,浪费了一个数组空间——即定义数组长度为k,但我实际只能放k - 1个就显示满了,所以我们要想放k个元素就要定义k + 1个长度的数组
}
 
/**
* 入队
* @param value
* @return 插入成功返回true,失败返回false
*/
public boolean enQueue(int value) {
    if (isFull()) {  // 如果当前队列满了,就不能再入队了
        return false;
    }
    else {
        elem[rear] = value;  // 再当前的数组尾巴下标下新增数据
        // 新增数据后要对尾巴下标进行更新
        rear = (rear + 1) % elem.length; // 注意这里,防止数组越界
    }
    return true;
}
 
/**
* 出队
* @return 出队成功返回true,失败返回false
*/
public boolean deQueue() {
    if (isEmpty()) {
        return false;
    }
    else {
        front = (front + 1) % elem.length;
    }
    return true;
}
 
/**
* 得到队头元素
* @return 返回队头元素,如果队列为空返回-1
*/
public int Front() {
    if (isEmpty()) {
        return -1;
    }
    return elem[front];
}
 
/**
* 得到队尾元素 返回队尾元素,如果队列为空返回-1
有小伙伴可能会说,直接返回elem[rear - 1]不就好了吗?但如果此时rear == 0, 数组下标不就越界了吗?
但此时如果此时队列不为空,rear等于0说明当前的队尾下标是 数组长度-1,因为是循环队列呀
* @return
*/
public int Rear() {
    if (isEmpty()) return -1;
    int index = (rear == 0) ? elem.length - 1 : rear - 1;
    return elem[index];
}
 
/**
* 当前循环队列是否为空
* @return
*/
public boolean isEmpty() {
    if (rear == front) return true; // 他们相遇证明是空的
    return false;
}
 
/**
* 判断当前队列是否为满
* @return 满了返回true, 不满返回false
*/
public boolean isFull() {
    // 因为我们空了一个,相当于有一个数组下标没有用到,所以说比如我定义了一个长度为4的数组,我就只能放3个元素(就显示数组满了)
    if ((rear + 1) % elem.length == front) { // 空一个格子,和一开始rear == front 为空的情况区分开来
        return true;
    }
    return false;
    }
}

4.2 用栈实现队列

题目链接:力扣 

思路:

我们入栈的时候把数据都放到s1中

*即用s1来存放数据,s2用来输出数据,如果s2为空,就把s1的数据全部放到s2中

*意思就是push的存的数据都放到了s1中,我们pop输出的数据都是从s2里输出的;

💖代码实现:

class MyQueue {
    Stack<Integer> s1; 
    Stack<Integer> s2;
    public MyQueue() {
        s1 = new Stack<>();
        s2 = new Stack<>();
    }
    // 入队
    public void push(int x) {
        s1.push(x);
    }
    // 出队
    public int pop() {
         // 我们输出数据都是从s2里拿的,如果s2为空,就把s1里的元素放到s2里面
        if (s2.empty()) {
            while (!s1.empty()) { // 把s1里的数据全部放到s2里面
                int tmp = s1.pop();
                s2.push(tmp);
            }
            return s2.pop();
        }
        else {
            return s2.pop();
        }
    }
    // 返回队列开头的元素
    public int peek() {
        // 和上面出队的操作相似,不同的是在对s2进行出栈操作时,只是获取栈顶元素的值,而不是删除当前栈顶
        if (s2.empty()) {
            while (!s1.empty()) { 
                int tmp = s1.pop();
                s2.push(tmp);
            }
            return s2.peek();
        }
        else {
            return s2.peek();
        }
    }
    // 判断当前队列是否为空,空返回true,非空返回false
    public boolean empty() {
        return s1.empty() && s2.empty();
    }
}

4.3 最小栈  

题目链接:力扣 

 分析

在本题中,push,pop,top等操作功能和普通的栈都相同,不同的就是多了一个getMin()获取栈中元素最小值的操作;

思路

为了让我们能够在常数时间内检测到当前栈中的最小元素,我们不妨用两个栈s1和minStack,s1栈就正常的进行数据的存放和处理,另一个minStack栈就专门存放当前栈中的最小元素,并随着s1栈中元素的变化不断的进行更新;

💖代码实现:

class MinStack {
    Stack<Integer> s1;
    Stack<Integer> minStack;
 
    public MinStack() {
        s1 = new Stack<>();
        minStack = new Stack<>();
    }
    // 入栈
    public void push(int val) {
        s1.push(val); // 把元素都先放到s1栈中
        if (minStack.empty()) { // 如果当前最小栈minStack中为空,直接把当前元素放到minStack就行
            minStack.push(val);
        }
        else if (val <= minStack.peek()){ // 下面有详细解释
            minStack.push(val);
        }
    }
    
    public void pop() {
        int x = s1.pop();
        // 当s1中要出栈的元素等于最小栈中的元素,即此时栈中元素的最小值已经发生了变化,所以最小栈中元素也要出栈
        if (x == minStack.peek()) {
            minStack.pop();
        }
    }
    
    public int top() {
        return s1.peek();
    }
    
    public int getMin() {
        if (!minStack.empty()) {
            return minStack.peek();    
        }
        return -1;
    }
}

对其中一些代码的解释:

// 入栈
    public void push(int val) {
        s1.push(val); // 把元素都先放到s1栈中
        if (minStack.empty()) { // 如果当前最小栈minStack中为空,直接把当前元素放到minStack就行
            minStack.push(val);
        }
        // 如果minStack不为空,就需要进行判断,
       // 如果新添加的元素比minStack中的元素小或等于就放进去
        else if (val <= minStack.peek()){ 
            minStack.push(val);
        }
    }
为啥要包含等于这种情况
比如s1栈中放的是0、1、0,如果没有等于minStack中存放的只有一个0,
但如果s1中的元素发生了变化——栈顶元素0出栈了,按我们下面出栈的规则来说的话,
此时minStack中的0也要出栈,此时栈不就为空了吗?

总结

今天的内容就介绍到这里,下一节我们将介绍有关二叉树的有关内容;让我们下一期内容再见!!

 

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

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

相关文章

日撸 Java 三百行day36

文章目录 day36 领接表1. 领接表知识点2.结合上图去抽象一个邻接表结点的对象3.广度优先遍历4.深度优先遍历 #说明 闵老师的文章链接&#xff1a; 日撸 Java 三百行&#xff08;总述&#xff09;_minfanphd的博客-CSDN博客 自己也把手敲的代码放在了github上维护&#xff1a;h…

Vue3+Three.js+antvG2实战项目 智慧城市(三)

前言 在网上找了很久都没有找到使用Three.js开发智慧城市的免费文章或者免费视频,自己花了一点时间做了一个纯前端的智慧城市项目。 技术栈都是最新的:vue3vitetypeScriptThreeantv G2 源码分享 源码 模型,天空图盒子链接分享(不想下载源码可以只用下这个)提取码1234 20230424_…

【iOS】—— KVC再学习

KVC 文章目录 KVCKVC常用的四种方法 key和keyPath的区别用法&#xff1a; 批量存值操作批量赋值操作字典模型相互转化KVC的其他方法 KVC原理探索setValue:forKey: 的原理&#xff08;KVC赋值原理&#xff09;valueForKey:的原理&#xff08;KVC取值原理&#xff09; 注意事项 K…

C++数据结构:手撕AVL树

目录 一. 什么是AVL树 二. AVL树的节点定义 三. AVL树的插入操作 3.1 寻找插入位置 3.2 更新平衡因子 3.3 AVL树的旋转调整 3.4 AVL树插入操作的整体实现 四. AVL树的检验 附录&#xff1a;AVL树的实现完整代码 AVL树定义代码 -- AVLTree.h AVL树检验代码 -- test.…

你了解PostProcessor机制吗?

Spring框架对于后置处理器的最佳实践 PostProcessor译为后置处理器&#xff0c;大多数开发人员都使用过springboot对后置处理器的实例级别实践&#xff0c;也就是BeanPostProcessor接口。其实spring还提供了两种容器级别的实践&#xff1a;BeanDefinitionRegistryPostProcesso…

今天试了试chatgpt

今天试了试chatgpt&#xff0c;真是服了 arcade&#xff1f; Arcade是一个Python游戏开发库&#xff0c;它提供了一系列的工具和函数&#xff0c;可以帮助开发者快速地创建2D游戏。以下是Arcade的一些特点&#xff1a; 简单易用&#xff1a;Arcade提供了简单易用的API&#x…

egg3.0连接egg-mongoose插入一条数据、插入多条数据

插入一条数据 app/router.js use strict;/*** param {Egg.Application} app - egg application*/ module.exports app > {const { router, controller } app;router.get(/, controller.home.index);router.get(/role, controller.role.index);router.post(/role/add, co…

【ChatGPT】如何用十分钟部署一个属于自己的chatgpt网站

&#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#xff1a;Zeeland&#x1f4da; Github主页: Undertone0809 (Zeeland) (github.com)&…

【两个月算法速成】day03-链表

目录 203. 移除链表元素 题目链接 思路 代码 206. 反转链表 题目链接 思路 代码 总结 203. 移除链表元素 题目链接 力扣 思路 如下图所示就是移除链表的过程 但是值得注意的是&#xff0c;移除头节点和其他位置的节点是不一样的&#xff0c;以为头结点前面没有节点。…

每日学术速递4.24

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Collaborative Diffusion for Multi-Modal Face Generation and Editing(CVPR 2023) 标题&#xff1a;多模态人脸生成和编辑的协同扩散 作者&#xff1a;Ziqi Huang, Kelvin C.K. …

Vue3进阶使用详解(node.js、Vue3路由基础项目、axios的使用详细(实现数据分页---前后端分离)、axios加载失败)

Vue3进阶使用详解(node.js、Vue3路由基础项目、axios的使用详细(实现数据分页—前后端分离)、axios加载失败) Vue cli CLI是Commond-Line Interface&#xff0c;翻译为命令界面&#xff0c;又称脚手架。VueCLI是一个官方发布vue.js项目脚手架。使用VueCLI可以快速搭建vue开发…

【IAR工程】STM8S基于ST标准库读取DHT11数据

【IAR工程】STM8S基于ST标准库读取DHT11数据 ✨申明&#xff1a;本文章仅发表在CSDN网站&#xff0c;任何其他网站&#xff0c;未注明来源&#xff0c;见此内容均为盗链和爬取&#xff0c;请多多尊重和支持原创!&#x1f341;对于文中所提供的相关资源链接将作不定期更换。&…

HTTP协议 GET和POST区别 请求响应 Fiddler postman ajax

&#x1f496; 欢迎来阅读子豪的博客&#xff08;JavaEE篇 &#x1f934;&#xff09; &#x1f449; 有宝贵的意见或建议可以在留言区留言 &#x1f4bb; 欢迎 素质三连 点赞 关注 收藏 &#x1f9d1;‍&#x1f680;码云仓库&#xff1a;补集王子的代码仓库 不要偷走我小火…

Mac下nvm安装使用

​欢迎光临我的博客查看最新文章: https://river106.cn 1、简介 nvm 是 Mac 下的 node.js 管理工具。可以通过 nvm 安装和切换不同版本的 node.js。 官网&#xff1a;https://nvm.uihtm.com/ github&#xff1a;https://github.com/nvm-sh/nvm 2、安装 curl -o- https://raw…

移动端适配rem方案

做移动端的适配我们就是要考虑&#xff0c;对于不同大小的手机屏幕&#xff0c;怎么动态改变页面布局中所有盒子的宽度高度、字体大小等。 这个问题我们可以使用相对单位rem。 那么什么是 rem&#xff1f; rem&#xff08;font size of the root element&#xff09;是指相对…

Linux-中断和时间管理(上)

目录 中断的进入过程 中断的进入过程 为方便实验&#xff0c;本章以配套的目标板 FS4412为例来介绍 Linux 的中断子系统&#xff0c;并且编写相应的中断处理程序。FS4412 上的处理器是 SAMSUNG公司的 Exynos4412&#xff0c;该处理器使用的是4核的 Cortex-A9&#xff0c;&…

c++Lambda匿名函数

cLambda匿名函数 &#xff08;1&#xff09; 定义a. [外部变量方位方式说明符]b. (参数)c. mutabled.noexcept/throw()e.->返回值类型f.函数体 2&#xff09;c11中的拉姆达表达式中的&#xff08;&#xff09;可以省略吗 所谓匿名函数&#xff0c;简单地理解就是没有名称的函…

《C++ Primer Plus》(第6版)第17章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第17章编程练习 《C Primer Plus》&#xff08;第6版&#xff09;第17章编程练习1. 计算输入流中第一个\$之前的字符数目2. 将键盘输入&#xff08;直到模拟的文件尾&#xff09;复制到通过命令行指定的文件中3. 将一个文件复制…

完全免费的基于区块链和 IPFS 的去中心化博客平台

一、前言 xLog是一个基于Crossbell区块链的博客解决方案&#xff0c;专注于Web3数据由用户掌控。Crossbell是一个基于Web3技术的去中心化博客平台&#xff0c;用户可以在该平台上发布文章并进行交流和创作。社区提供多种交流平台和有奖创作活动。 xLog是基于 Crossbell 区块链…

【AI回复】“我问它,你对五一调休怎么看”

前言 马上就要到五一啦&#xff0c;放假打算去哪里玩呢&#xff1f; “我肯定是宅在家里写博客啊” 最近五一调休在某博上引起大家的共鸣&#xff0c;看了评论那叫一个惨不忍睹哇。 因为我比较对AI感兴趣&#xff0c;所以想看看它是怎么看待调休的。 首先&#xff0c;在百度…