Java数据结构和算法(五)——队列
前面一篇博客我们讲解了并不像数组一样完全作为存储数据功能,而是作为构思算法的辅助工具的数据结构——栈,本篇博客我们介绍另外一个这样的工具——队列。栈是后进先出,而队列刚好相反,是先进先出。
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数据结构和算法(五)——队列的更多相关文章
- Java数据结构和算法(五)——队列
队列.queue,就是现实生活中的排队. 1.简单队列: public class Queqe { private int array[]; private int front; private in ...
- Java数据结构和算法(五)--希尔排序和快速排序
在前面复习了三个简单排序Java数据结构和算法(三)--三大排序--冒泡.选择.插入排序,属于算法的基础,但是效率是偏低的,所以现在 学习高级排序 插入排序存在的问题: 插入排序在逻辑把数据分为两部分 ...
- Java数据结构和算法(二)--队列
上一篇文章写了栈的相关知识,而本文会讲一下队列 队列是一种特殊的线性表,在尾部插入(入队Enqueue),从头部删除(出队Dequeue),和栈的特性相反,存取数据特点是:FIFO Java中queu ...
- 【Java数据结构学习笔记之二】Java数据结构与算法之队列(Queue)实现
本篇是数据结构与算法的第三篇,本篇我们将来了解一下知识点: 队列的抽象数据类型 顺序队列的设计与实现 链式队列的设计与实现 队列应用的简单举例 优先队列的设置与实现双链表实现 队列的抽象数据类型 ...
- 【Java数据结构学习笔记之三】Java数据结构与算法之队列(Queue)实现
本篇是数据结构与算法的第三篇,本篇我们将来了解一下知识点: 队列的抽象数据类型 顺序队列的设计与实现 链式队列的设计与实现 队列应用的简单举例 优先队列的设置与实现双链表实现 队列的抽象数据类型 ...
- Java数据结构与算法(3):队列
队列也是一种表,不同的是队列在一端进行插入而在另一端进行删除. 队列模型 队列的基本操作包括入队.出队操作.在表的末端插入元素,在表的开头删除元素,即先进先出(FIFO). 队列的数组实现 对于每一个 ...
- 1、java数据结构和算法---循环队列
直接上代码: public class CircleArrayQueueLvcai { private int[] array; private int maxSize;//循环队列大小 privat ...
- Java数据结构和算法 - 栈和队列
Q: 栈.队列与数组的区别? A: 本篇主要涉及三种数据存储类型:栈.队列和优先级队列,它与数组主要有如下三个区别: A: (一)程序员工具 数组和其他的结构(栈.队列.链表.树等等)都适用于数据库应 ...
- Java数据结构和算法(六)——前缀、中缀、后缀表达式
前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...
- Java数据结构和算法(十四)——堆
在Java数据结构和算法(五)——队列中我们介绍了优先级队列,优先级队列是一种抽象数据类型(ADT),它提供了删除最大(或最小)关键字值的数据项的方法,插入数据项的方法,优先级队列可以用有序数组来实现 ...
随机推荐
- Linux下简单C语言小程序的反汇编分析
韩洋原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在开始,本文为因为参加MOO ...
- 为什么String类是不可变的?
为什么String类是不可变的? String类 什么是不可变对象 当满足以下条件时,对象才是不可变的: 对象创建以后其状态就不能修改. 对象的所有域都是final类型的. 对象是正确创建的(在对象的 ...
- LeetCode 121. Best Time to Buy and Sell Stock (买卖股票的最好时机)
Say you have an array for which the ith element is the price of a given stock on day i. If you were ...
- maven基本基础知识及命令学习-1
Maven概述:Maven是很有效的项目管理工具,maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目构建.报告和文档的软件项目管理工具.统一管理环境,架包等. 一 maven下载 ...
- 使用 Hadoop 进行语料处理(面试题)
原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7751741.html ------------------------------------ ...
- CSS 备忘
border-radius : 10px / 40px 10表示X轴半径 40表示Y轴半径 font:italic bold 13px/13px arial,sans-serif; ...
- 使用Docker运行Microsoft SQL Server 2017
最近每天都在空闲时间努力编写Apworks框架的案例代码WeText.在文本发布和处理微服务中,我打算使用微软的SQL Server for Linux来做演示,于是也就在自己的docker-comp ...
- 转:ef获取某个表中的部分字段值
我有个新闻表 id,title,body,createtime,author,click 使用ef4.1 仅仅读取 id,title,createtime 并显示在页面上. public static ...
- 初识React-Redux之粗暴理解入门
权当暂记 日后再行补充完善,若有阅读者,请翻到下文黄色标题'从这里开始'起阅读. Rudex在我看来最本质做的事情就是将所有的State属性统一存储(一个属性就是一个注册到store的Reducer) ...
- 表空间与数据文件Offline,online的区别
首先明确,表空间与数据文件的关系:Oracle数据库表空间有两种,一种smallfile小文件表空间(默认),另一种bigfile大文件表空间: 默认表空间与数据文件的关系:允许一对多的处理方式,一个 ...