队列
-
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
-
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表 (百度百科)
-
队列分为顺序队列和循环队列,按实现方式又分为数组实现的队列和链表实现的队列。按数组实现,需要静态分配内存空间即指定该队列能存储多少个元素,具体来说,需要一个maxSize来控制队列的大小。对于数组模拟的队列,若不加以控制,会出现溢出和假溢出。在队列的形成过程中,也可以利用线性链表的原理,来生成一个队列。基于链表的队列,要动态创建和删除节点,效率较低,但是可以动态增长。因此,基于链表的队列理论上不会出现溢出现象。
数组实现的线性队列
通过数组来模拟线性队列,实现十分简单。我们新建一个类,包含以下属性和方法
属性/方法 | 解释 |
---|---|
int arr[] | 用于模拟队列的数组 |
int front | 指向队头的索引,初始化为-1 |
int rear | 指向队尾的索引,初始化为-1 |
int maxSize | 队列最大存储的元素/对象个数 |
boolean isFull() | 队列是否为满 |
boolean isEmpty() | 队列是否为空 |
void add(int val) | 入队一个元素val |
int get() | 出队一个元素 |
int peek() | 查看一个元素但不出队 |
void show() | 显示队列中的所有元素 |
- 队满的判断条件:rear == maxSize-1
- 队空的判断条件:rear == front
- 入队:arr[++rear] = val
- 出队:return arr[++front]
Java代码
package com.like.java.data_structure.queue;
/*
* 通过数组来模拟顺序队列
*/
public class ArrayQueue {
// 最大容量
private int maxSize;
private int front;
private int rear;
// 用于存放队列的数组
private int[] arr;
// 构造器
public ArrayQueue(int maxSize)
{
this.maxSize = maxSize;
// front指向队列头的前一个位置
front = -1;
rear = -1;
arr = new int[maxSize];
}
// 判断队列是否为满
public boolean isFull()
{
return rear == (maxSize-1);
}
// 判断队列是否为空
public boolean isEmpty()
{
return front == rear;
}
// 入队
public void add(int val)
{
if(isFull())
{
System.err.println("队列已满");
return;
}
rear ++;
arr[rear] = val;
}
// 出队
public int get()
{
if(isEmpty())
{
// System.err.println("队列为空!");
// 通过抛出异常来处理
throw new RuntimeException("Empty Queue.");
}
front++;
return arr[front];
}
// 遍历
public void show()
{
// 判断是否为空
if(isEmpty())
{
throw new RuntimeException("Empty Queue.");
}
for(int i=0;i<arr.length;i++)
{
System.out.printf("%d\t",arr[i]);
}
System.out.println();
}
// 显示队列头部
public int peek()
{
if(isEmpty())
{
throw new RuntimeException("Empty Queue");
}
return arr[front+1];
}
}
测试用例
package com.like.java.data_structure.queue;
import java.util.Scanner;
public class ArrayQueueTester {
public static void main(String[] args) {
// 创建一个队列对象
ArrayQueue aqQueue = new ArrayQueue(3);
Scanner scanner = new Scanner(System.in);
char c = ' ';
boolean loop = true;
System.err.println("s(show queue)");
System.err.println("a(add val)");
System.err.println("e(exit)");
System.err.println("g(get a val)");
System.err.println("h(get header)");
while(loop)
{
try {
Thread.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
System.out.print("Command->");
c = scanner.next().charAt(0);
switch(c)
{
case 's':
aqQueue.show();
break;
case 'a':
System.out.print("Give a integet number:");
int val = scanner.nextInt();
aqQueue.add(val);
break;
case 'e':
scanner.close();
System.out.println("Exit 0");
loop = false;
System.exit(0);
case 'g':
try {
int res = aqQueue.get();
System.out.println("Get:"+res);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = aqQueue.peek();
System.out.println("Peek header:"+res);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
default:
System.out.println("Wrong command");
}
}
}
}
数组实现循环队列
顺序队列不可避免的会出现假溢出现象,不能达到复用的效果。因此,我们需要引进循环队列这种改进的结构,用取模的方式来解决假溢出。
主要思想如下:
- 运用取模操作来进行判断队列为满的优化
- front指向当前队头元素,front = 0
- rear指向队尾元素的下一个位置,因为想空出一个空间作为约定,牺牲这个空间来判满,rear = 0.
- 满:(rear+1)%maxSize == front
- 空: rear == front
- 队列中有效元素的个数:(rear+maxSize-front)%maxSize / +maxSize来应对rear<front的情况
改进后的Java代码:
package com.like.java.data_structure.queue;
public class CircleArrayQueue {
// 最大容量
private int maxSize;
private int front;
private int rear;
// 用于存放队列的数组
private int[] arr;
// 构造器
public CircleArrayQueue(int maxSize)
{
this.maxSize = maxSize;
// front指向队列头
front = 0;
rear = 0;
arr = new int[maxSize];
}
// 判断队列是否为满
public boolean isFull()
{
return (rear+1)%maxSize == front;
}
// 判断队列是否为空
public boolean isEmpty()
{
return front == rear;
}
// 入队
public void add(int val)
{
if(isFull())
{
System.err.println("队列已满");
return;
}
arr[rear] = val;
rear = (rear+1)%maxSize;
}
// 出队
public int get()
{
if(isEmpty())
{
// System.err.println("队列为空!");
// 通过抛出异常来处理
throw new RuntimeException("Empty Queue.");
}
// 1. 先将front的值保存到一个临时变量
// 2.front后移,考虑取模
// 3. 将临时保存的变量保存
int res = arr[front];
front = (front+1)%maxSize;
return res;
}
// 遍历
public void show()
{
// 判断是否为空
if(isEmpty())
{
throw new RuntimeException("Empty Queue.");
}
// 从front开始遍历(rear+maxSize-front)%maxSize个元素
for(int i=front;i<front+size();i++)
{
System.out.printf("arr[%d] = %d\t",i%maxSize,arr[i%maxSize]);
}
System.out.println();
}
// 当前队列有效个数
public int size()
{
return (rear+maxSize-front)%maxSize;
}
// 显示队列头部
public int peek()
{
if(isEmpty())
{
throw new RuntimeException("Empty Queue");
}
return arr[front%maxSize];
}
}
当然,在实际的项目中,我们可以使用java自带工具包中的队列来进行草所。java.util.Queue。
包含的主要方法:
方法 | 解释 |
---|---|
offer(object x) /add(object x) | 入队 |
poll()/remove() | 出队 |
element()/peek() | 查看第一个元素,不出队 |
Java代码- 可以参照以下示例
Java 实例 - 队列(Queue)用法