队列的定义和实现
前言
- 语言:Java
- 环境:IntelliJ IDEA
- JDK版本:1.8
- 源码:GitHub
队列的定义
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列的特点:
- 先进先出(FIFO),插入数据从队尾插入,取出数据从队头取出
- 无论是数组还是链表实现,通常需要两个变量(指针)来标记队头和队尾
队列的实现
数组实现队列
数组实现队列的几种情况:
由上面四种情况可得到以下信息:
- 初始值
front=rear=-1
- front指向第一个数据的前面,rear直接指向最后一个数据
- 判断队列为空的条件是
front==rear
- 判断队列满的条件是
rear==maxSize-1
- 有效数据个数为
rear-front
- 入队时先增加rear,再在rear处插入数据
- 出队时先增加front,再在front处取出数据
public class ArrayQueue {
private Integer maxSize = 3; //队列最大可容纳对象数
private Integer front = -1; //始终指向第一个数据之前
private Integer rear = -1; //指向最后一个数据
private Employee[] employeeQueue; //存放数据的数组
public ArrayQueue(){
employeeQueue = new Employee[this.maxSize];
}
public ArrayQueue(Integer maxSize){
this.maxSize = maxSize;
employeeQueue = new Employee[this.maxSize];
}
/**
* 添加一个数据,添加到尾部,添加成功返回true,失败返回false
*/
public boolean addEmployee(Employee employee){
if(this.isFull()){
return false;
}
this.rear++; //队列添加数据时,将last需要后移
this.employeeQueue[this.rear] = employee;
return true;
}
/*
* 获取第一条数据,获取后会从队列中移除
*/
public Employee getEmployee(){
if(this.isEmpty()){
return null;
}
this.front++; //移除数据实际上是将first后移,使其无法访问前面的数组
return this.employeeQueue[this.front];
}
/**
* 显示第一条数据,仅显示,不会从队列移除
*/
public Employee showEmployee(){
if(this.isEmpty()){
return null;
}
return this.employeeQueue[this.front+1];
}
/**
* 队列是否为空
*/
public boolean isEmpty(){
return this.front == this.rear;
}
/**
* 队列是否已满
*/
public boolean isFull(){
return this.rear == this.maxSize-1;
}
/**
* 将队列的所有数据格式化显示
*/
public String formatQueue(){
if(this.isEmpty()){
return "[]";
}
String str = "";
for(int i = this.front;i<this.rear;i++){
str += this.employeeQueue[i+1].toString()+"\n";
}
return str;
}
}
优点:
- 容易理解,便于实现
缺点:
- 队列容纳的数据数量有限制
- 为一次性使用的队列,每取出数据,则之前的位置就无法再次使用,如果将该队列插入满数据,再全部取出,那么虽然该队列理论上为空,但实际队列容量已经为0
环形数组实现队列
第一种实现方式: 当front和rear移动至maxSize-1时,将其再移动到数组的开始,以此循环使用
由上面四种情况可得到以下信息:
- 初始值
front=rear=0
- front始终指向第一个数据,rear指向最后一个数据后的空间
- 判断队列为空的条件是
front==rear
- 判断队列满的条件是
(rear+1)%maxSize==front
- 有效数据个数为
(rear+maxSize-front)%maxSize
- 入队时直接在rear处插入数据,再使rear后移(后移需要取模)
- 出队时直接取出front处的数据,再使front后移(后移需要取模)
public class CircleArrayQueue1 {
private Integer maxSize = 3; //队列最大可容纳对象数
private Integer front = 0; //指向第一个数据
private Integer rear = 0; //指向最后一个数据的后一个空区域
private Employee[] employeeQueue; //存放数据的数组
public CircleArrayQueue1(){
employeeQueue = new Employee[this.maxSize];
}
public CircleArrayQueue1(Integer maxSize){
this.maxSize = maxSize;
employeeQueue = new Employee[this.maxSize];
}
/**
* 添加一个数据,添加到尾部,添加成功返回true,失败返回false
*/
public boolean addEmployee(Employee employee){
if(this.isFull()){
return false;
}
this.employeeQueue[this.rear] = employee;
this.rear = (this.rear+1)%this.maxSize;
return true;
}
/*
* 获取第一条数据,获取后会从队列中移除
*/
public Employee getEmployee(){
if(isEmpty()){
return null;
}
Employee employee = this.employeeQueue[this.front];
this.front = (this.front+1)%this.maxSize;
return employee;
}
/**
* 显示第一条数据,仅显示,不会从队列移除
*/
public Employee showEmployee(){
if(isEmpty()){
return null;
}
Employee employee = this.employeeQueue[this.front];
return employee;
}
/**
* 队列是否为空
*/
public boolean isEmpty(){
return this.rear == this.front;
}
/**
* 队列是否已满
*/
public boolean isFull(){
return (this.rear+1)%this.maxSize == this.front;
}
/**
* 将队列的所有数据格式化显示
*/
public String formatQueue(){
if(isEmpty()){
return "[]";
}
String str = "";
for (int i = this.front;i<this.front+(this.rear + this.maxSize - this.front)%this.maxSize ;i++){
str += employeeQueue[i%this.maxSize]+"\n";
}
return str;
}
}
优点:
- 解决了移出数据后空间无法再次使用的问题
缺点:
- 队列容纳的数据数量有限制
- 会空出一个位置,即当队列最大容量为6时,实际只能插入5个数据
第二种实现方式: front和rear不再被maxSize限制,只有当使用front和rear时,通过maxSize寻找其在数组中的实际位置
由上面四种情况可得到以下信息:
- 初始值
front=rear=-1
- front始终指向第一个数据的前一个位置,rear指向最后一个数据
- 判断队列为空的条件是
front==rear
- 判断队列满的条件是
rear-front=maxSize
- 有效数据个数为
rear-front
- 入队时先增加rear,再在rear处插入数据
- 出队时先增加front,再取出front处的数据
public class CircleArrayQueue2 {
private Integer maxSize = 3; //队列最大可容纳对象数
private Integer front = -1; //始终指向第一个数据之前
private Integer rear = -1; //始终指向最后一个数据
private Employee[] employeeQueue; //存放数据的数组
public CircleArrayQueue2(){
employeeQueue = new Employee[this.maxSize];
}
public CircleArrayQueue2(Integer maxSize){
this.maxSize = maxSize;
employeeQueue = new Employee[this.maxSize];
}
/**
* 添加一个数据,添加到尾部,添加成功返回true,失败返回false
*/
public boolean addEmployee(Employee employee){
if(this.isFull()){
return false;
}
this.rear++;
this.employeeQueue[this.rear%this.maxSize] = employee;
return true;
}
/*
* 获取第一条数据,获取后会从队列中移除
*/
public Employee getEmployee(){
if(isEmpty()){
return null;
}
this.front++;
return this.employeeQueue[this.front%this.maxSize];
}
/**
* 显示第一条数据,仅显示,不会从队列移除
*/
public Employee showEmployee(){
if(isEmpty()){
return null;
}
Employee employee = this.employeeQueue[(this.front+1)%this.maxSize];
return employee;
}
/**
* 队列是否为空
*/
public boolean isEmpty(){
return this.rear == this.front;
}
/**
* 队列是否已满
*/
public boolean isFull(){
return this.rear-this.front==this.maxSize;
}
/**
* 将队列的所有数据格式化显示
*/
public String formatQueue(){
if(isEmpty()){
return "[]";
}
String str = "";
for (int i = this.front+1;i<=this.rear ;i++){
str += employeeQueue[i%this.maxSize]+"\n";
}
return str;
}
}
优点:
- 解决了移出数据后空间无法再次使用的问题
- 解决了队列会出现空余位置的问题
缺点:
- 队列容纳的数据数量有限制
链表实现队列
链表实现队列的几种情况:
由上面四种情况可得到以下信息:
- 初始值
last=head
- head的next指向第一条数据,last直接指向最后一条数据
- 判断队列为空的条件是
head.next==null
- 有效数据个数为
length
- 入队时将last.next指向新数据,再将last指向新数据
- 出队时先保存head.next,再将head.next指向取出的数据的next
public class LinkQueue {
public int length = 0; //统计有效队列中的数据个数
private Node head; //队列的头部
private Node last; //队列的尾部
public LinkQueue(){
head = new Node();
this.last = this.head;
}
/**
* 添加一个数据,添加到尾部,添加成功返回true,失败返回false
*/
public boolean addEmployee(Employee employee){
Node node = new Node();
node.data = employee;
this.last.next = node;
this.last = node;
this.length ++;
return true;
}
/*
* 获取第一条数据,获取后会从队列中移除
*/
public Employee getEmployee(){
if(isEmpty()){
return null;
}
Node temp = this.head.next;
this.head.next = temp.next;
length --;
return temp.data;
}
/**
* 显示第一条数据,仅显示,不会从队列移除
*/
public Employee showEmployee(){
if(isEmpty()){
return null;
}
Node temp = this.head.next;
return temp.data;
}
/**
* 队列是否为空
*/
public boolean isEmpty(){
return this.head.next == null;
}
/**
* 将队列的所有数据格式化显示
*/
public String formatQueue(){
if(isEmpty()){
return "[]";
}
String str = "";
Node temp = this.head.next;
while (true){
str += temp.data.toString() + "\n";
if(temp.next==null){
break;
}
temp = temp.next;
}
return str;
}
class Node{
private Employee data;
private Node next;
}
}
优点:
- 队列容量不受限制
- 队列不会出现空余位置
- 利用JVM的垃圾回收机制,可以合理的利用系统资源