您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

看完这篇你还不知道这些队列,我这些图白作了

时间:2019-09-02 10:58:51  来源:  作者:

队列(queue)是一种采用先进先出(FIFO)策略的抽象数据结构,即最先进队列的数据元素,同样要最先出队列。队列跟我们排队买票一样,先来排队的肯定先买票,后来排队的的后买到票。队列如下图所示:

看完这篇你还不知道这些队列,我这些图白作了

 

队列有两个重要的概念,一个叫队头,一个叫队尾,队头指向的是第一个元素,而队尾指向的是最后一个元素。队列跟栈一样也是访问受限制的,所以队列也只有两个主要的操作:入队(enqueue)操作 和 出队(dequeue)操作 。入队操作就是将一个元素添加到队尾,出队操作就是从队头取出一个元素。

队列的底层实现可以用数组和链表,基于数组实现的队列叫作顺序队列,基于链表实现的队列叫作链式队列,下面我们分别用数组和链表来简单的实现这两种队列。

基于数组的队列

不管使用那种方式来实现队列,都需要定义两个指针分别指向队头和队尾,本文中我们用head指向队头,tail指向队尾,后面的示例中这将默认使用这个,有特殊的地方我会进行说明,先来看看顺序队列的入队、出队操作。

看完这篇你还不知道这些队列,我这些图白作了

 

图中可以看出,入队时,队尾往后移动,队头保持不变,出队是队头往后移动,队尾保持不变。入队、出队操作的逻辑都比较简单,可能你有疑问的地方是:出队时为什么队头要往后移动而不是一直指向数组下标为0的位置? 为什么呢?如果我们保持队头一直指向数组下标为0的位置,那每次出队操作后,后面的数据都需要往前挪一位,换句话说每次出队操作都需要进行数据迁移,而数据迁移的代价比较大,每次数据迁移的时间复杂度为O(n),这样会极大的影响队列的使用性能。如果我们出队时,队头往后移动一位,这样我们就避免每次出队都进行数据迁移,我们只需要在只有在tail等于数组大小且head不等于0时,进行一次数据迁移,将已经出队留下的空间继续供入队时使用。下图是数据迁移的过程:

看完这篇你还不知道这些队列,我这些图白作了

 

数据迁移时,从head位置开始的数据都需要往前移动head位,这样就把出队后的空间腾出来,供后续入队操作使用。

基于数组的队列实现代码:

/**
 * 基于数组的队列
 */
public class ArrayQueue {
 // 存放数据的数组
 private String[] items;
 // 容器的大小
 private int size = 0;
 // 第一个节点
 private int head = 0;
 // 最后一个节点
 private int tail = 0;
 // 构造函数
 public ArrayQueue(int size){
 this.size = size;
 items = new String[size];
 }
 /**
 * 入队操作
 * @param data
 * @return
 */
 public int enqueue(String data){
 // 如果最后一个节点等于容器大小,说明队列满了
 /**
 * 判断队列满了的条件,tail = size,head = 0,
 */
 if (tail == size && head == 0) return -1;
 /**
 * 如果tail = size,但是head != 0,说明前有数据删除,队列未满,需要数据迁移
 */
 if (tail == size){
 // head 后面的数据都需要往前迁移 head 位
 for (int i= head;i< size;i++){
 items[i-head] = items[i];
 }
 // 将最后一个元素迁移 head 位
 tail -=head;
 // 第一个元素指向 0
 head = 0;
 }
 // 向队列中添加元素
 items[tail] = data;
 tail++;
 return 1;
 }
 /**
 * 出队操作
 * @return
 */
 public String dequeue(){
 // 第一个元素和最后一个元素相等时,队列为空
 if (head == tail) return null;
 String result = items[head];
 // 第一个元素后移一次,这样做的好处是在出队时不需要数据迁移
 head ++ ;
 return result;
 }
}

链式队列

链式队列实现起来相对顺序队列来说要简单很多,我们先来看看链式队列的入队、出队操作:

看完这篇你还不知道这些队列,我这些图白作了

 

从图中可以看出链式队列入队操作是将tail的next指向新增的节点,然后将tail指向新增的节点,出队操作时,将head节点指向head.next节点。链式队列与顺序队列比起来不需要进行数据的迁移,但是链式队列增加了存储成本。

基于链表的队列实现代码

/**
 * 基于链表的队列
 */
public class LinkQueue {
 // 指向队首
 private Node head;
 // 指向队尾
 private Node tail;
 /**
 * 入队操作
 * @param data
 * @return
 */
 public int enqueue(String data){
 Node node = new Node(data,null);
 // 判断队列中是否有元素
 if (tail == null) {
 tail = node;
 head = node;
 }else {
 tail.next = node;
 tail = node;
 }
 return 1;
 }
 /**
 * 出队操作
 * @return
 */
 public String dequeue(){
 if (head==null) return null;
 String data = head.data;
 head = head.next;
 // 取出元素后,头指针为空,说明队列中没有元素,tail也需要制为空
 if (head == null){
 tail = null;
 }
 return data;
 }
 class Node{
 private String data;
 private Node next;
 public Node(String data,Node node){
 this.data = data;
 next = node;
 }
 }
}

循环队列

循环队列是对顺序队列的改进,因为顺序队列不可避免的数据迁移操作,数据迁移操作会导致队列的性能下降,为了避免这个问题,将队列改造成循环的,当tail到达数组的最大下标时,重新指回数组下标为0的位置,这样就避免了数据迁移。先来看看循环队列的出队、入队操作:

看完这篇你还不知道这些队列,我这些图白作了

 

因为队列是循环队列,所以在进行入队、出队操作时,就不能像顺序队列那样对tail、head进行简单的加1操作,我们需要对tail、head加1后与数组的大小进行求余操作,来得出tail、head的值,这样才能进行循环操作。循环队列需要牺牲一个存储空间,对于一个存储空间为n的循环队列来说只能存放n-1为数据,因为如果不牺牲一个存储空间的话,当tail==head时,就有可能存在队空或者队满的情况。

循环队列的实现代码

/**
 * 环形队列,不需要数据迁移,提高性能
 */
public class CircularQueue {
 // 存放数据的数组
 private String[] items;
 // 容器的大小
 private int size = 0;
 // 第一个节点
 private int head = 0;
 // 最后一个节点
 private int tail = 0;
 // 构造函数
 public CircularQueue(int size){
 this.size = size;
 items = new String[size];
 }
 /**
 * 入队操作
 * @param data
 * @return
 */
 public int enqueue(String data){
 /**
 * 判断环形队列满了的条件,(tail+1)求余等于head
 */
 if ((tail+1)%size == head) return -1;
 // 向队列中添加元素
 items[tail] = data;
 // 因为是环形队列,所以下边是数组长度的余数
 tail= (tail+1)%size;
 return 1;
 }
 /**
 * 出队操作
 * @return
 */
 public String dequeue(){
 // 第一个元素和最后一个元素相等时,队列为空
 if (head == tail) return null;
 String result = items[head];
 // 因为是环形队列,所以下边是数组长度的余数
 head = (head+1)% size ;
 return result;
 }
}

双端队列

双端队列是一种队头、队尾都可以进行入队、出队操作的队列,双端队列采用双向链表来实现,先来看一下双端队列的入队、出队操作:

看完这篇你还不知道这些队列,我这些图白作了

 

可以从动态图中看出,双端队列的每一端都是一个栈,都符合栈先进后出的特性,如果我们对双端队列进行禁止队头入队和队尾出队操作的限制,双端队列又变成了一个链式队列,双端队列是一种多功能的数据结构,我们可以使用它来提供队列和栈两种功能。

双端队列的实现代码

/**
 * 双端队列,使用双向链表实现
 */
public class DoubleEndsQueue {
 private static class Node {
 String item;
 Node next;
 Node prev;
 Node(Node prev, String element, Node next) {
 this.item = element;
 this.next = next;
 this.prev = prev;
 }
 }
 // 第一个节点
 private Node first;
 // 最后一个节点
 private Node last;
 /*
 * 在第一个节点前面入队
 */
 public void enqueueFirst(String e) {
 final Node f = first;
 final Node newNode = new Node(null, e, f);
 // 第一个节点指向新节点
 first = newNode;
 if (f == null)
 // 最后一个节点也指向该节点
 last = newNode;
 else
 // 当前节点的前节点指向新节点
 f.prev = newNode;
 }
 /**
 * 在最后一个元素后面入队
 * @param e
 */
 public void enqueueLast(String e) {
 final Node l = last;
 final Node newNode = new Node(l, e, null);
 // 最后一个节点指向新节点
 last = newNode;
 if (l == null)
 // 第一个节点指向新节点
 first = newNode;
 else
 // 当前节点的下节点指向新节点
 l.next = newNode;
 }
 /**
 * 从第一个节点出队
 * @return
 */
 public String dequeueFirst() {
 if (first == null) return null;
 final Node f = first;
 String element = f.item;
 Node next = f.next;
 f.item = null;
 f.next = null;
 // 第一个节点指向当先节点的next节点
 first = next;
 if (next == null)
 // 说明队列为空
 last = null;
 else
 next.prev = null;
 return element;
 }
 /**
 * 从最后节点出队
 * @return
 */
 public String dequeueLast() {
 final Node l = last;
 if (last == null) return null;
 String element = l.item;
 Node prev = l.prev;
 l.item = null;
 l.prev = null;
 last = prev;
 if (prev == null)
 first = null;
 else
 prev.next = null;
 return element;
 }
 
 // 输出队列全部内容
 public void displayAll() {
 while (first !=null){
 System.out.print(first.item+" ");
 first = first.next;
 }
 System.out.println("===============");
 }
}

优先队列

优先队列为一种不必遵循队列先进先出(FIFO)特性的特殊队列,优先队列跟普通队列一样都只有一个队头和一个队尾并且也是从队头出队,队尾入队,不过在优先队列中,每次入队时,都会按照入队数据项的关键值进行排序(从大到小、从小到大),这样保证了关键字最小的或者最大的项始终在队头,出队的时候优先级最高的就最先出队,这个就像我们医院就医一样,急救的病人要比普通的病人先就诊。一起来看看优先队列的出队、入队操作:

看完这篇你还不知道这些队列,我这些图白作了

 

在示例中,我们规定数值越小优先级越高。我们每执行一次入队操作时,小的元素都会靠近头队,在出队的时候,元素小的也就先出队。

优先队列的代码实现

这里使用的数组实现优先队列,用数组实现主要原因是更好理解优先队列的思想。一般都是使用堆来实现优先队列,因为数组实现在插入的时候对数据的排序代价比较大。

/**
 * 优先队列
 */
public class PriorityQueue {
 // 存放数据的数组
 private Integer[] items;
 // 容器的大小
 private int size = 0;
 // 第一个节点
 private int head = 0;
 // 构造函数
 public PriorityQueue(int size){
 this.size = size;
 items = new Integer[size];
 }
 /**
 * 入队操作
 * @param data
 * @return
 */
 public int enqueue(Integer data){
 int j;
 if (head == 0){
 items[head++] = data;
 }
 else {
 for (j=head-1;j>=0;j--){
 // 将小的数往后排
 if (data > items[j]){
 items[j+1] = items[j];
 }else {
 break;
 }
 }
 items[j+1] = data;
 head++;
 }
 return 1;
 }
 public Integer dequeue(){
 return items[--head];
 }
}

总结

  • 队列是一种遵循先进先出(FIFO)的数据结构
  • 队列可以使用数组和链表实现,数组实现叫作顺序队列,链表实现叫作链式队列
  • 循环队列解决了顺序队列的数据迁移带来的性能损耗的问题
  • 双端队列是队头和队尾都可以进行入队、出队操作的队列
  • 优先队列是一种不必遵循先进先出规则的队列,任意元素加入时,都会讲优先级最高的放入到队头


Tags:队列   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
TCP握手的时候维护的队列 半连接队列(SYN队列) 全连接队列(accepted队列)半连接队列是什么?服务器收到客户端SYN数据包后,Linux内核会把该连接存储到半连接队列中,并响应SYN+ACK报...【详细内容】
2021-12-21  Tags: 队列  点击:(10)  评论:(0)  加入收藏
前言栈和队列是一对好兄弟,前面我们介绍过一篇栈的文章(栈,不就后进先出),栈的机制相对简单,后入先出,就像进入一个狭小的山洞,山洞只有一个出入口,只能后进先出(在外面的先出去,堵...【详细内容】
2021-09-16  Tags: 队列  点击:(67)  评论:(0)  加入收藏
在Linux上做网络应用的性能优化时,一般都会对TCP相关的内核参数进行调节,特别是和缓冲、队列有关的参数。很多文章会告诉你需要修改哪些参数,但我们经常是知其然而不知其所以然...【详细内容】
2021-06-11  Tags: 队列  点击:(114)  评论:(0)  加入收藏
Redis的 list 数据结构常用来作为 异步消息队列 使用,使用 rpush/lpush 操作 入队 ,使用 lpop/rpop 来操作 出队 > rpush my-queue apple banana pear(integer) 3> llen my-qu...【详细内容】
2021-04-15  Tags: 队列  点击:(165)  评论:(0)  加入收藏
队列比较队列队列比较总结:就性能而言,无锁(什么也不加) > CAS > LOCK;从现实使用中考虑,我们一般选择有界队列(避免生产者速度过快,导致内存溢出);同时,为了减少Java的垃圾回收对系...【详细内容】
2020-12-15  Tags: 队列  点击:(136)  评论:(0)  加入收藏
1、 查找Docker容器中的RabbitMQ镜像docker ps -a[root@linux ~]# docker ps -aCONTAINER ID IMAGE COMMAND CREATED...【详细内容】
2020-11-27  Tags: 队列  点击:(222)  评论:(0)  加入收藏
队列与堆栈类似,只是插入点与移除点不同。我们在队列的一端添加,从另一端移除。这一次,我们称之为先进先出(FIFO)。就像你能想到的任何队列一样,例如在餐厅、迪厅或者当你在等待进...【详细内容】
2020-11-17  Tags: 队列  点击:(68)  评论:(0)  加入收藏
在之前的线程池的介绍中我们看到了很多阻塞队列,这篇文章我们主要来说说阻塞队列的事。 阻塞队列也就是 BlockingQueue ,这个类是一个接 口,同时继承了 Queue 接口,这两个接口...【详细内容】
2020-11-16  Tags: 队列  点击:(57)  评论:(0)  加入收藏
背景“生产者和消费者模型” 是多线程通信的典型案例,本章节将利用前一节的锁和条件队列的知识,来实现一个完整的有界缓冲区,并创建多个线程访问该有界缓冲区,模拟生产者提供数...【详细内容】
2020-11-10  Tags: 队列  点击:(118)  评论:(0)  加入收藏
今天就从Linux源码的角度看下Server端的Socket在进行listen的时候到底做了哪些事情(基于Linux 3.10内核),当然由于listen的backlog参数和半连接hash表以及全连接队列都相关,在...【详细内容】
2020-11-04  Tags: 队列  点击:(156)  评论:(0)  加入收藏
▌简易百科推荐
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(3)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(2)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(10)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(20)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(25)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(25)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条