用数组实现队列(顺序队列&循环队列)
用数组实现队列(顺序队列&循环队列)
顺序队列
️
队列(先进先出)

几个问题:
- 队列方法:入队、出队
- 队列的存储:即队首队尾两个指针,
- 扩容:如果队列容量不够了,应该扩容,如果队尾没有位置了,队首有位置,应该把元素往前移
主要是上面三个问题,在代码中都有体现,上面的扩容方法借鉴了ArrayList的扩容方法。
package com.helius.structure.queue;
import java.util.Arrays;
/**
* 用数组实现一个队列,即顺序队列
*/
public class ArrayQueue {
// 存储数据的数组
private Object[] elements;
//队列大小
private int size;
// 默认队列容量
private int DEFAULT_CAPACITY = 10;
// 队列头指针
private int head;
// 队列尾指针
private int tail;
private int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8;
/**
* 默认构造函数 初始化大小为10的队列
*/
public ArrayQueue(){
elements = new Object[DEFAULT_CAPACITY];
initPointer(0,0);
}
/**
* 通过传入的容量大小创建队列
* @param capacity
*/
public ArrayQueue(int capacity){
elements = new Object[capacity];
initPointer(0,0);
}
/**
* 初始化队列头尾指针
* @param head
* @param tail
*/
private void initPointer(int head,int tail){
this.head = head;
this.tail = tail;
}
/**
* 元素入队列
* @param element
* @return
*/
public boolean enqueue(Object element){
ensureCapacityHelper();
elements[tail++] = element;//在尾指针处存入元素且尾指针后移
size++;//队列元素个数加1
return true;
}
private void ensureCapacityHelper() {
if(tail==elements.length){//尾指针已越过数组尾端
//判断队列是否已满 即判断数组中是否还有可用存储空间
//if(size<elements.length){
if(head==0){
//扩容
grow(elements.length);
}else{
//进行数据搬移操作 将数组中的数据依次向前挪动直至顶部
for(int i= head;i<tail;i++){
elements[i-head]=elements[i];
}
//数据搬移完后重新初始化头尾指针
initPointer(0,tail-head);
}
}
}
/**
* 扩容
* @param oldCapacity 原始容量
*/
private void grow(int oldCapacity) {
int newCapacity = oldCapacity+(oldCapacity>>1);
if(newCapacity-oldCapacity<0){
newCapacity = DEFAULT_CAPACITY;
}
if(newCapacity-MAX_ARRAY_SIZE>0){
newCapacity = hugeCapacity(newCapacity);
}
elements = Arrays.copyOf(elements,newCapacity);
}
private int hugeCapacity(int newCapacity) {
return (newCapacity>MAX_ARRAY_SIZE)? Integer.MAX_VALUE:newCapacity;
}
/**
* 出队列
* @return
*/
public Object dequeue(){
if(head==tail){
return null;//队列中没有数据
}
Object obj=elements[head++];//取出队列头的元素且头指针后移
size--;//队列中元素个数减1
return obj;
}
/**
* 获取队列元素个数
* @return
*/
public int getSize() {
return size;
}
}
测试用例
public class TestArrayQueue {
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(4);
//入队列
queue.enqueue("helius1");
queue.enqueue("helius2");
queue.enqueue("helius3");
queue.enqueue("helius4");
//此时入队列应该走扩容的逻辑
queue.enqueue("helius5");
queue.enqueue("helius6");
//出队列
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
//此时入队列应该走数据搬移逻辑
queue.enqueue("helius7");
//出队列
System.out.println(queue.dequeue());
//入队列
queue.enqueue("helius8");
//出队列
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
//入队列
queue.enqueue("helius9");
queue.enqueue("helius10");
queue.enqueue("helius11");
queue.enqueue("helius12");
//出队列
System.out.println(queue.dequeue());
System.out.println(queue.dequeue());
}
}
结果:
helius1
helius2
helius3
helius4
helius5
helius6
helius7
helius8
null
helius9
helius10
循环队列
用java实现循环队列的方法:
增加一个属性size用来记录目前的元素个数。目的是当head=rear的时候,通过size=0还是size=数组长度,来区分队列为空,或者队列已满。
数组中只存储数组大小-1个元素,保证rear转一圈之后不会和head相等,也就是队列满的时候,rear+1=head,中间刚好空一个元素。
当rear=head的时候,一定是队列空了。
队列(Queue)两端允许操作的类型不一样:
可以进行删除的一端称为队头,这种操作也叫出队dequeue;
可以进行插入的一端称为队尾,这种操作也叫入队enqueue。
队列的示意图

实现队列时,要注意的是假溢出现象,如上图的最后一幅图。
如图所示的假溢出现象,顺序队列可以如此,循环队列我们可以让这个尾指针指向front前面的元素,这也正符合我们想要的循环队列的定义。

解决办法:使用链式存储,这显然可以。在顺序存储时,我们常见的解决办法是把它首尾相接,构成循环队列,这可以充分利用队列的存储空间。
循环队列示意图:

在上图中,front指向队列中第一个元素,rear指向队列队尾的下一个位置。
但依然存在一个问题:当front和rear指向同一个位置时,这代表的是队空还是队满呢?大家可以想象下这种情景。
解决这种问题的常见做法是这样的:
使用一标记,用以区分这种易混淆的情形。
牺牲一个元素空间。当front和rear相等时,为空;当rear的下一个位置是front时,为满。
如下图:


下面我们给出循环队列,并采用第二种方式,即牺牲一个元素空间来区分队空和队满的代码.
几个重点:
1、front指向队头,rear指向队尾的下一个位置。
2、队为空的判断:frontrear;队为满的判断:(rear+1)%MAXSIZEfront。
上面说的rear即为代码中的的tail
/**
* 使用数组实现循环队列
* @author Helius
*/
public class CirculiQueue {
//存储队列数据的数组
private Object[] elements;
//默认数组容量
private int DEFAULT_CAPACITY=10;
//队列中元素个数
private int size;
// 队列头指针
private int head;
//队列尾指针
private int tail;
/**
* 默认构造函数
*/
public CirculiQueue(){
elements = new Object[DEFAULT_CAPACITY];
}
/**
* 通过传入的容量参数构造队列
* @param capacity
*/
public CirculiQueue(int capacity){
elements = new Object[capacity];
}
/**
* 元素入队列
* @param element
* @return
*/
public boolean enqueue(Object element){
//判断队列是否已满
if(head == (tail+1)%elements.length){
//队列已满
return false;
}
//将元素存入tail位置上
elements[tail]=element;
//尾指针后移
/*tail++;
if(tail==elements.length){
tail = 0;
}*/
tail = (tail+1)%elements.length;
size++;
return true;
}
/**
* 元素出队列
* @return
*/
public Object dequeue(){
//判断队列是否为空
if(head==tail){
return null;
}
//获取head位置上的元素
Object element = elements[head];
//头指针后移
/*head++;
if(head==elements.length){
head = 0;
}*/
head = (head+1)%elements.length;
size--;
return element;
}
/**
* 获取队列大小
* @return
*/
public int getSize() {
return size;
}
}
这里也添加了size属性,可以稍做修改,来实现第一种方式。
用数组实现队列(顺序队列&循环队列)的更多相关文章
- 39 _ 队列5 _ 循环队列需要几个参数来确定 及其含义的讲解.swf
上面讲解都是循环队列,如果是链表实现的话就很简单,队列只有循环队列才比较复杂 此时队列中只存储一个有效元素3,当在删除一个元素的时候,队列为空,pFont向上移动,pFont等于pRear,但是此时p ...
- 三 基于Java数组手写循环队列
Code: package dataStucture2.stackandqueue; /** * 手写循环队列 * * @param <E> */ public class MyLoopQ ...
- javascript实现数据结构与算法系列:队列 -- 链队列和循环队列实现及示例
1 队列的基本概念 队列(Queue):也是运算受限的线性表.是一种先进先出(First In First Out ,简称FIFO)的线性表.只允许在表的一端进行插入,而在另一端进行删除. 队首(fr ...
- c++实验5 顺序/链式队列
链式队列及循环队列 1.循环队列的实现(请采用模板类及模板函数实现) [实现提示] 同时可参见教材p65-p67页的ADT描述及算法实现及ppt)函数.类名称等可自定义,部分变量请加上学号后3位.也可 ...
- 循环队列+堆优化dijkstra最短路 BZOJ 4152: [AMPPZ2014]The Captain
循环队列基础知识 1.循环队列需要几个参数来确定 循环队列需要2个参数,front和rear 2.循环队列各个参数的含义 (1)队列初始化时,front和rear值都为零: (2)当队列不为空时,fr ...
- 《Java数据结构与算法》笔记-CH4-5不带计数字段的循环队列
第四章涉及三种数据存储类型:栈,队列,优先级队列 1.概括:他们比数组和其他数据存储结构更为抽象,主要通过接口对栈,队列和优先级队列进行定义.这些 接口表明通过他们可以完成的操作,而他们的主要实现机制 ...
- 数据结构:循环队列(C语言实现)
生活中有非常多队列的影子,比方打饭排队,买火车票排队问题等,能够说与时间相关的问题,一般都会涉及到队列问题:从生活中,能够抽象出队列的概念,队列就是一个能够实现"先进先出"的存储结 ...
- 循环队列的C语言实现
生活中有很多队列的影子,比如打饭排队,买火车票排队问题等,可以说与时间相关的问题,一般都会涉及到队列问题:从生活中,可以抽象出队列的概念,队列就是一个能够实现“先进先出”的存储结构.队列分为链式队列和 ...
- LeetCode 622——设计循环队列
1. 题目 设计你的循环队列实现. 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环.它也被称为"环形缓冲器". 循环队列 ...
- TypeScript算法与数据结构-队列和循环队列
本文涉及的源码,均在我的github.有两部分队列和循环队列.有问题的可以提个issue,看到后第一时间回复 1. 队列(Queue) 队列也是一种线性的数据结构, 队列是一种先进先出的数据结构.类似 ...
随机推荐
- HTML & CSS & JavaScript 从一个表格到一个灰阶颜色表 04
工具1:HBuilder X 1.9.9.20190522 工具2:火狐浏览器 67.0.4 (64 位) 目前,我们已经将一些行和列插入到表格中,并设置单元格的背景颜色,显示 RGB 值等. 例 7 ...
- ls: 显示目下的内容及相关属性信息
ls: 显示目下的内容及相关属性信息 [功能说明] ls 命令可以理解为英文单词 "list" 的缩写,其功能是列出目录的内容及其内容属性信息(list directory con ...
- MeteoInfoLab脚本示例:MODIS AOD
MODIS的气溶胶光学厚度(AOD)产品应用很广,数据可以在Giovanni上下载:http://disc.sci.gsfc.nasa.gov/giovanni/overview/index.html ...
- ES6的7个实用技巧
Hack #1 交换元素 利用数组解构来实现值的互换 let a = 'world', b = 'hello' [a, b] = [b, a] console.log(a) // -> hell ...
- 【源码项目+解析】C语言/C++开发,打造一个小项目扫雷小游戏!
一直说写个几百行的小项目,于是我写了一个控制台的扫雷,没有想到精简完了代码才200行左右,不过考虑到这是我精简过后的,浓缩才是精华嘛,我就发出来大家一起学习啦,看到程序跑起来能玩,感觉还是蛮有成就感的 ...
- Linux发行版教你如何选 给入门者的选择通法
Linux的发行版何止琳琅满目,简直是乱入你眼. 本篇将介绍选择发行版的经验和通用法则,主要会从PC角度去谈. 更新于2020年,初次发布于2017年 选择发行版需考虑哪些因素 选择发行版时需要考虑的 ...
- lumen中间件 Middleware
app/http 下新建 TestMiddleware.php <?php namespace App\Http\Middleware; use Closure; class TestMiddl ...
- Spring源码解析之基础应用(三)
组合Java配置 在XML中,我们可以使用<import/>标签,在一个XML文件中引入另一个XML文件,在Java类中,我们同样可以在一个配置类中用@Import引入另一个配置类,被引入 ...
- Windows Server 2003 Enterprise Edition SP2
SN: MPQ6X-3MCCF-47H9T-TKC2F-T69WM
- django—模板相关
关于在html文件中使用模板语言时,一些符号的含义 {{}}:显示变量相关,在模板渲染的时候替换成值 {%%}:处理逻辑相关 django模板语言中,对于列表字典等容器类型,不能够通过[ 索引/键值 ...