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

几个问题:
- 队列方法:入队、出队
- 队列的存储:即队首队尾两个指针,
- 扩容:如果队列容量不够了,应该扩容,如果队尾没有位置了,队首有位置,应该把元素往前移
主要是上面三个问题,在代码中都有体现,上面的扩容方法借鉴了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) 队列也是一种线性的数据结构, 队列是一种先进先出的数据结构.类似 ...
随机推荐
- 利用rtklib处理GPS以及北斗数据详解
利用rtklib开源代码处理GPS以及北斗数据详解 在GNSS领域最基础的工作是这些GNSS系统的定位工作,对于绝大多数研究者,自己着手完成这些工作是一个"鸡肋":完全独立设计的话 ...
- 浅谈BSGS
用于求解形如\(a^x≡b\mod p\)的最小非负整数解\(x\). 由欧拉定理\(a^{\phi(p)}≡1\mod p\)可以知道,我们找的解如果有解则一定在\(\phi(p)\)范围内,而最大 ...
- RHSA-2018:1700-重要: procps-ng 安全更新(存在EXP、本地提权)
[root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 修复命令: 使用root账号登陆She ...
- 安装mariadb/mysql 连接失败问题
在linux下安装mariadb会出现一系列问题 问题1->服务器端不需要用户名密码就可登陆数据库 问题2->php使用mysql不能连接数据库 访问受限 问题3->navicate ...
- JProfiler 教程 使用说明
JProfiler (本文原创转载请注明) 简介 JProfiler是一个重量级的JVM监控工具,提供对JVM精确监控,其中堆遍历.CPU剖析.线程剖析看成定位当前系统瓶颈的得力工具.可以统计压 ...
- intelliJ 软件项目打开运行
1.导入项目 2.首先更改数据库,找到application-dev.yml文件,更改数据源 3.配置tomcat端口 找到application.yml 文件 然后打开pom.xml 更改版本号 ...
- 第一期chrome浏览器的小技巧------《提高搜索效率》
!!! 这次的技巧是:利用chrome提供的设置 提高你的搜索效率 !!! 我们经常遇到问题,搜索的时候很不方便 比如你在百度上搜索一个东西的时候正好没有搜到,那么你想找到这个东西的话,很明显要到其他 ...
- beego log
package main import ( "github.com/astaxie/beego/logs" _ "xcms/routers" _ "x ...
- java: 非法字符: '\ufeff'
错误问题记录: Error:(1, 1) java: 非法字符: '\ufeff' Error:(1, 1) 错误: 需要class, interface或enum报错 问题发生时因为编码问题导致,如 ...
- Hibernate关系映射之many-to-many(多对多)
在表设计中,我们一般都会考虑表与表之间的关系,现在我来介绍一下表与表之间的几种对应关系many-to-many 多对多 比如一个用户可以有多种角色 一种角色可以对用多个不同的用户所以角色和用户之间的关 ...