前面一篇博客我们讲解了并不像数组一样完全作为存储数据功能,而是作为构思算法的辅助工具的数据结构——栈,本篇博客我们介绍另外一个这样的工具——队列。栈是后进先出,而队列刚好相反,是先进先出。

1、队列的基本概念

  队列(queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

  队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。

  比如我们去电影院排队买票,第一个进入排队序列的都是第一个买到票离开队列的人,而最后进入排队序列排队的都是最后买到票的。

  在比如在计算机操作系统中,有各种队列在安静的工作着,比如打印机在打印列队中等待打印。

  队列分为:

  ①、单向队列(Queue):只能在一端插入数据,另一端删除数据。

  ②、双向队列(Deque):每一端都可以进行插入数据和删除数据操作。

  这里我们还会介绍一种队列——优先级队列,优先级队列是比栈和队列更专用的数据结构,在优先级队列中,数据项按照关键字进行排序,关键字最小(或者最大)的数据项往往在队列的最前面,而数据项在插入的时候都会插入到合适的位置以确保队列的有序。

2、Java模拟单向队列实现

  在实现之前,我们先看下面几个问题:

  ①、与栈不同的是,队列中的数据不总是从数组的0下标开始的,移除一些队头front的数据后,队头指针会指向一个较高的下标位置,如下图:

  

  ②、我们再设计时,队列中新增一个数据时,队尾的指针rear 会向上移动,也就是向下标大的方向。移除数据项时,队头指针 front 向上移动。那么这样设计好像和现实情况相反,比如排队买电影票,队头的买完票就离开了,然后队伍整体向前移动。在计算机中也可以在队列中删除一个数之后,队列整体向前移动,但是这样做效率很差。我们选择的做法是移动队头和队尾的指针。

  ③、如果向第②步这样移动指针,相信队尾指针很快就移动到数据的最末端了,这时候可能移除过数据,那么队头会有空着的位置,然后新来了一个数据项,由于队尾不能再向上移动了,那该怎么办呢?如下图:

  

  为了避免队列不满却不能插入新的数据,我们可以让队尾指针绕回到数组开始的位置,这也称为“循环队列”。

  

  弄懂原理之后,Java实现代码如下:

package com.ys.datastructure;

public class MyQueue {
private Object[] queArray;
//队列总大小
private int maxSize;
//前端
private int front;
//后端
private int rear;
//队列中元素的实际数目
private int nItems; public MyQueue(int s){
maxSize = s;
queArray = new Object[maxSize];
front = 0;
rear = -1;
nItems = 0;
} //队列中新增数据
public void insert(int value){
if(isFull()){
System.out.println("队列已满!!!");
}else{
//如果队列尾部指向顶了,那么循环回来,执行队列的第一个元素
if(rear == maxSize -1){
rear = -1;
}
//队尾指针加1,然后在队尾指针处插入新的数据
queArray[++rear] = value;
nItems++;
}
} //移除数据
public Object remove(){
Object removeValue = null ;
if(!isEmpty()){
removeValue = queArray[front];
queArray[front] = null;
front++;
if(front == maxSize){
front = 0;
}
nItems--;
return removeValue;
}
return removeValue;
} //查看对头数据
public Object peekFront(){
return queArray[front];
} //判断队列是否满了
public boolean isFull(){
return (nItems == maxSize);
} //判断队列是否为空
public boolean isEmpty(){
return (nItems ==0);
} //返回队列的大小
public int getSize(){
return nItems;
} }

  测试:

package com.ys.test;

import com.ys.datastructure.MyQueue;

public class MyQueueTest {
public static void main(String[] args) {
MyQueue queue = new MyQueue(3);
queue.insert(1);
queue.insert(2);
queue.insert(3);//queArray数组数据为[1,2,3] System.out.println(queue.peekFront()); //1
queue.remove();//queArray数组数据为[null,2,3]
System.out.println(queue.peekFront()); //2 queue.insert(4);//queArray数组数据为[4,2,3]
queue.insert(5);//队列已满,queArray数组数据为[4,2,3]
} }

  

3、双端队列

  双端队列就是一个两端都是结尾或者开头的队列, 队列的每一端都可以进行插入数据项和移除数据项,这些方法可以叫做:

  insertRight()、insertLeft()、removeLeft()、removeRight()

  如果严格禁止调用insertLeft()和removeLeft()(或禁用右端操作),那么双端队列的功能就和前面讲的栈功能一样。

  如果严格禁止调用insertLeft()和removeRight(或相反的另一对方法),那么双端队列的功能就和单向队列一样了。

4、优先级队列

  优先级队列(priority queue)是比栈和队列更专用的数据结构,在优先级队列中,数据项按照关键字进行排序,关键字最小(或者最大)的数据项往往在队列的最前面,而数据项在插入的时候都会插入到合适的位置以确保队列的有序。

  优先级队列 是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有:

  (1)查找

  (2)插入一个新元素

  (3)删除

  一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 。对于优先权相同的元素,可按先进先出次序处理或按任意优先权进行。

  这里我们用数组实现优先级队列,这种方法插入比较慢,但是它比较简单,适用于数据量比较小并且不是特别注重插入速度的情况。

  后面我们会讲解堆,用堆的数据结构来实现优先级队列,可以相当快的插入数据。

  数组实现优先级队列,声明为int类型的数组,关键字是数组里面的元素,在插入的时候按照从大到小的顺序排列,也就是越小的元素优先级越高。

package com.ys.datastructure;

public class PriorityQue {
private int maxSize;
private int[] priQueArray;
private int nItems; public PriorityQue(int s){
maxSize = s;
priQueArray = new int[maxSize];
nItems = 0;
} //插入数据
public void insert(int value){
int j;
if(nItems == 0){
priQueArray[nItems++] = value;
}else{
j = nItems -1;
//选择的排序方法是插入排序,按照从大到小的顺序排列,越小的越在队列的顶端
while(j >=0 && value > priQueArray[j]){
priQueArray[j+1] = priQueArray[j];
j--;
}
priQueArray[j+1] = value;
nItems++;
}
} //移除数据,由于是按照大小排序的,所以移除数据我们指针向下移动
//被移除的地方由于是int类型的,不能设置为null,这里的做法是设置为 -1
public int remove(){
int k = nItems -1;
int value = priQueArray[k];
priQueArray[k] = -1;//-1表示这个位置的数据被移除了
nItems--;
return value;
} //查看优先级最高的元素
public int peekMin(){
return priQueArray[nItems-1];
} //判断是否为空
public boolean isEmpty(){
return (nItems == 0);
} //判断是否满了
public boolean isFull(){
return (nItems == maxSize);
} }

  insert() 方法,先检查队列中是否有数据项,如果没有,则直接插入到下标为0的单元里,否则,从数组顶部开始比较,找到比插入值小的位置进行插入,并把 nItems 加1.

  remove 方法直接获取顶部元素。

  优先级队列的插入操作需要 O(N)的时间,而删除操作则需要O(1) 的时间,后面会讲解如何通过 堆 来改进插入时间。

5、总结

  本篇博客我们介绍了队列的三种形式,分别是单向队列、双向队列以及优先级队列。其实大家听名字也可以听得出来他们之间的区别,单向队列遵循先进先出的原则,而且一端只能插入,另一端只能删除。双向队列则两端都可插入和删除,如果限制双向队列的某一段的方法,则可以达到和单向队列同样的功能。最后优先级队列,则是在插入元素的时候进行了优先级别排序,在实际应用中单项队列和优先级队列使用的比较多。后面讲解了堆这种数据结构,我们会用堆来实现优先级队列,改善优先级队列插入元素的时间。

  通过前面讲的栈以及本篇讲的队列这两种数据结构,我们稍微总结一下:

  ①、栈、队列(单向队列)、优先级队列通常是用来简化某些程序操作的数据结构,而不是主要作为存储数据的。

  ②、在这些数据结构中,只有一个数据项可以被访问。

  ③、栈允许在栈顶压入(插入)数据,在栈顶弹出(移除)数据,但是只能访问最后一个插入的数据项,也就是栈顶元素。

  ④、队列(单向队列)只能在队尾插入数据,对头删除数据,并且只能访问对头的数据。而且队列还可以实现循环队列,它基于数组,数组下标可以从数组末端绕回到数组的开始位置。

  ⑤、优先级队列是有序的插入数据,并且只能访问当前元素中优先级别最大(或最小)的元素。

  ⑥、这些数据结构都能由数组实现,但是可以用别的机制(后面讲的链表、堆等数据结构)实现。

Java数据结构和算法(五)——队列的更多相关文章

  1. Java数据结构和算法(五)——队列

    队列.queue,就是现实生活中的排队. 1.简单队列: public class Queqe { private int array[]; private int front; private in ...

  2. Java数据结构和算法(五)--希尔排序和快速排序

    在前面复习了三个简单排序Java数据结构和算法(三)--三大排序--冒泡.选择.插入排序,属于算法的基础,但是效率是偏低的,所以现在 学习高级排序 插入排序存在的问题: 插入排序在逻辑把数据分为两部分 ...

  3. Java数据结构和算法(二)--队列

    上一篇文章写了栈的相关知识,而本文会讲一下队列 队列是一种特殊的线性表,在尾部插入(入队Enqueue),从头部删除(出队Dequeue),和栈的特性相反,存取数据特点是:FIFO Java中queu ...

  4. 【Java数据结构学习笔记之二】Java数据结构与算法之队列(Queue)实现

      本篇是数据结构与算法的第三篇,本篇我们将来了解一下知识点: 队列的抽象数据类型 顺序队列的设计与实现 链式队列的设计与实现 队列应用的简单举例 优先队列的设置与实现双链表实现 队列的抽象数据类型 ...

  5. 【Java数据结构学习笔记之三】Java数据结构与算法之队列(Queue)实现

      本篇是数据结构与算法的第三篇,本篇我们将来了解一下知识点: 队列的抽象数据类型 顺序队列的设计与实现 链式队列的设计与实现 队列应用的简单举例 优先队列的设置与实现双链表实现 队列的抽象数据类型 ...

  6. Java数据结构与算法(3):队列

    队列也是一种表,不同的是队列在一端进行插入而在另一端进行删除. 队列模型 队列的基本操作包括入队.出队操作.在表的末端插入元素,在表的开头删除元素,即先进先出(FIFO). 队列的数组实现 对于每一个 ...

  7. 1、java数据结构和算法---循环队列

    直接上代码: public class CircleArrayQueueLvcai { private int[] array; private int maxSize;//循环队列大小 privat ...

  8. Java数据结构和算法 - 栈和队列

    Q: 栈.队列与数组的区别? A: 本篇主要涉及三种数据存储类型:栈.队列和优先级队列,它与数组主要有如下三个区别: A: (一)程序员工具 数组和其他的结构(栈.队列.链表.树等等)都适用于数据库应 ...

  9. Java数据结构和算法(六)——前缀、中缀、后缀表达式

    前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...

  10. Java数据结构和算法(十四)——堆

    在Java数据结构和算法(五)——队列中我们介绍了优先级队列,优先级队列是一种抽象数据类型(ADT),它提供了删除最大(或最小)关键字值的数据项的方法,插入数据项的方法,优先级队列可以用有序数组来实现 ...

随机推荐

  1. 一张表搞懂各种 Docker 监控方案 - 每天5分钟玩转 Docker 容器技术(86)

    前面我们已经介绍了ps/top/stats.Sysdig.Weave Scope.cAdvisor 和 Prometheus 多种容器监控工具和方案,是时候做一个比较了.下面将从五个方面来对比它们之间 ...

  2. $(window).on("load",function(){} 和 $(document).ready(function() {}

    $(window).on("load",function(){ //页面属性,图片,内容完全加载完,执行 } $(document).ready(function() { 或者$( ...

  3. LeetCode 604. Design Compressed String Iterator (设计压缩字符迭代器)$

    Design and implement a data structure for a compressed string iterator. It should support the follow ...

  4. LeetCode 153. Find Minimum in Rotated Sorted Array (在旋转有序数组中找到最小值)

    Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...

  5. LeetCode 27. Remove Element (移除元素)

    Given an array and a value, remove all instances of that value in place and return the new length. D ...

  6. Establish the LAMP (Linux+Apache+MySQL+PHP) Development Environment on Ubuntu 14.04 Operating System

    ######################################################## Step One: Update the software package in yo ...

  7. vue之地址栏#号问题

    mode的两个值 histroy:当你使用 history 模式时,URL 就像正常的 url,例如 http://jsapng.com/lms/,也好看! hash:默认'hash'值,但是hash ...

  8. Shell入门知识

    Shell 简介 Shell作为命令语言,它交互式地解释和执行用户输入的命令:作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支. 常常作为批处理命令来 ...

  9. kafka学习笔记2:生产者

    这次的笔记主要记录一下kafka的生产者的使用和一些重要的参数. 文中主要截图均来自kafka权威指南 主要涉及到两个类KafkaProducer和ProducerRecord. 总览 生产者的主要架 ...

  10. unity下跨平台excel读写

    这是以前写的跨windows和ios读写excel的工具,因为原来导表工具引用的第三方读写excel的dll只能在windos下使用,造成要在mac机器上跑PC端或者打包的时候,每次都要先在windo ...