1、队列介绍
2、队列的顺序存储(数组实现)
2.1队列的相关概念
2.2队列的操作
2.3代码实现
3、队列的链式存储(链表实现)
3.1链式队列的入队
3.2链式队列的出队
3.3代码实现:
4、循环队列
4.1顺序队列假溢出问题引出循环队列:
4.2何谓循环队列?
4.3循环队列的实现过程
队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front及 rear分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear则是随着数据输入而改变。
使用数组模拟队列示意图:
下面用两张图来理解入队和出队:
例如我们有一个存储整型元素的队列,我们依次入队:{1,2,3}:
添加元素时,元素只能从队尾一端进入队列,也即是2只能跟在1后面,3只能跟在2后面。
如果要出队:元素只能从队首出队列,出队列的顺序为:1、2、3,与入队时的顺序一致,这就是所谓的“先进先出”。
队列通常提供的操作:
package com.zhukun.ArrayListQueue;
import java.util.Scanner;
//使用数组模拟队列-编写一个ArrayQueue类
class ArrayQueue{
private int maxSize;//表示数组的最大容量
private int front;//队列头
private int rear;//队列尾
private int[] arr;//该数据用于存放数据,模拟队列
private int length=0;//队列中实际元素的数量
//创建队列构造器
public ArrayQueue(int arrMaxSize)
{
this.maxSize = arrMaxSize;
this.arr = new int[maxSize];
this.front = -1;//指向队列头部,分析出front是指向队列头的前一个位置
this.rear = -1;//指向队列的尾部,指向队列尾的数据(即就是队列最后一个数据)
}
//判断队列是否满
public boolean isFull()
{
return rear == maxSize-1;//如果成立则队满,即rear指向容量满足下最后一个数据
}
//判断是否为空
public boolean isEmpty()
{
return rear == front;//如果成立则对空,即rear指向队首front说明队列为空
}
//入队
public void addQueue(int num) {
//先判断队列是否为满
if(isFull()) {
System.out.println("队列已满,不能加入数据");
return;
}
rear++;//rear后移
arr[rear] = num;
}
//出队
public int getQueue() {
//判断队列是否为空
if(isEmpty()) {
//抛出异常
throw new RuntimeException("队列为空,不能取数据");
}
front++;//front 后移
return arr[front];
}
//显示队列的所有元素
public void showQueue() {
//遍历
if(isEmpty()) {
System.out.println("队列空的,没有数据~");
return;
}
for(int i=front+1;i
类似于使用链式结构保存线性表,也可以采用链式结构来保存队列的元素,采用链式存储结构的队列也被称为链队列。
对于链队列而言,由于程序需要从rear端添加元素,然后从front端删除元素,因此考虑对链队列增加front、rear两个引用变量,使他们分别指向链队列的头、尾两个节点。如下图所示:
注意:由于链队列采用链式存储结构保存数据元素,该队列允许添加无限多个数据元素,因此链队列不会出现列满的问题。
对于链队列而言,插入操作的实现非常简单,只要创建一个新节点,让原rear节点的next指向新节点,在让rear指向新节点即可。如下图所示链队列的插入操作:
对于链队列而言,删除操作的实现也是非常的简单,只要将原front节点指向原front节点的next节点,当然不要忘记释放原front节点的引用。如下图所示链队列的移除操纵:
package com.zhukun.Queue;
import java.util.Scanner;
class LinkQueue {
//创建一个私有的结点类,一个链式结点有数据域和指针域
private class Node{
private T data; //T类型的数据
private Node next; //结点类型的指针 指向下一个结点
//无参构造函数
public Node(){}
//有参构造函数
public Node(T element, Node next){
this.data = element;
this.next = next;
}
}
//代表链式队列的大小
private int size;
//链式队列的链队首
private Node front;
//链式队列的链队尾
private Node rear;
//初始化链式队列
//链式队列无参构造函数
public LinkQueue(){
size = 0;
front = null;
rear = null;
}
//链式队列含参构造函数
public LinkQueue(T element){
rear = new Node(element, null);
front = rear;
size ++;
}
//返回链式队列的长度
public int getLength(){
return size;
}
//判断队列是否为空
public boolean isEmpty(){
return size == 0;
}
//向rear端队尾插入元素,入队
public void addQueue(T element){
if(isEmpty()){
rear = new Node(element, null);
front = rear;
}else{
rear.next = new Node(element, null);
rear = rear.next;
}
size ++;
}
//从front端队首移除元素
public T removeQueue(){
if(isEmpty()){
throw new IndexOutOfBoundsException("链式队列为空异常");
}
Node oldNode = front;
front = front.next;
oldNode.next = null;//释放要移除的结点
size --;
return oldNode.data;
}
//返回链式队列的堆首元素,但不删除
public T headQueue(){
return front.data;
}
//清空链式队列
public void clear(){
front = null;
rear = null;
size = 0;
}
//遍历队列里的全部数据
public void showQueue() {
Node p = front;
while (p!= null)
{
System.out.println(p.data);
p=p.next;
}
}
}
public class LinkQueueDemo {
public static void main(String[] args)
{
//测试
//创建一个队列
LinkQueue queue = new LinkQueue();
int n;//接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
//输出一个菜单
while(loop)
{
System.out.println("1、输出队列");
System.out.println("2、退出程序");
System.out.println("3、入队");
System.out.println("4、出队");
System.out.println("5、查看队头数据");
System.out.println("6、输出队列的元素数量");
System.out.println("请输入你的选择:");
n = scanner.nextInt();
switch(n)
{
case 1:
queue.showQueue();
break;
case 2:
scanner.close();
queue.clear();
loop = false;
break;
case 3:
System.out.print("请输入要入队的数据:");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 4:
try {
int res = queue.removeQueue();
System.out.println("取出的数据是:"+res);
}catch(Exception e)
{
System.out.println(e.getMessage());
}
break;
case 5:
try {
int res = queue.headQueue();
System.out.println("队列头的数据为:"+res);
}catch(Exception e)
{
System.out.println(e.getMessage());
}
break;
case 6:
int l = queue.getLength();
System.out.println("队列中元素个数为:"+l);
break;
}
}
}
}
缺陷:假设当前队列分配的最大空间为6,队列处于(d)状态时不可再继续入队操作,但实际上数组中还有空的位置,这种现象就叫做“假溢出”,解决假溢出的途径--------采用循环队列
首先我们要说明的是循环队列仍然是基于数组实现的。但是为了形象化的说明问题,我们如下图所示:
我们分析可以发现当front==rear时可能队满也可能队空
此时我们人为浪费一个单元,令队满特征front=(rear+1)%maxSize ---------空闲单元法
代码实现:
package com.zhukun.Queue;
import java.util.Scanner;
//使用数组模拟队列-编写一个ArrayQueue类
class CircularArrayQueue{
private int maxSize;//表示数组的最大容量
private int front;//队列头
private int rear;//队列尾
private int[] arr;//该数据用于存放数据,模拟队列
private int length=0;//队列中实际元素的数量
//创建队列构造器
public CircularArrayQueue(int arrMaxSize)
{
this.maxSize = arrMaxSize;
this.arr = new int[maxSize];
this.front = 0;//指向队列头部,分析出front是指向队列头的前一个位置
this.rear = 0;//指向队列的尾部,指向队列尾的数据(即就是队列最后一个数据)
}
//判断队列是否满
public boolean isFull()
{
return (rear+1)%maxSize == front;//如果成立则队满,即rear指向容量满足下最后一个数据
}
//判断是否为空
public boolean isEmpty()
{
return rear == front;//如果成立则对空,即rear指向队首front说明队列为空
}
//入队
public void addQueue(int num) {
//先判断队列是否为满
if(isFull()) {
System.out.println("队列已满,不能加入数据");
return;
}
arr[rear] = num;
//将rear后移,这里要考虑取模
rear = (rear+1)%maxSize;
}
//出队
public int getQueue() {
//判断队列是否为空
if(isEmpty()) {
//抛出异常
throw new RuntimeException("队列为空,不能取数据");
}
//将front对应的值保留到一个临时变量,将front后移,考虑取模,将临时保存的变量返回
int value = arr[front];
front =(front +1)%maxSize;
return value;
}
//显示队列的所有元素
public void showQueue() {
//遍历
if(isEmpty()) {
System.out.println("队列空的,没有数据~");
return;
}
for(int i=front;i<(rear+maxSize)%maxSize;i++)
{
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
//显示队列的头数据,注意不是提取数据
public int headQueue() {
int num;//记录队首元素
//判断
if(isEmpty())
{
throw new RuntimeException("队列空的,没有数据");
}
num = arr[front];
return num;
}
//获取当前队列的元素个数
public int getLength()
{
return (rear+maxSize-front)%maxSize;
}
}
public class CircularArrayQueueDemo {
public static void main(String[] args)
{
//测试
//创建一个队列
ArrayQueue queue = new ArrayQueue(6);
int n;//接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
//输出一个菜单
while(loop)
{
System.out.println("1、输出队列");
System.out.println("2、退出程序");
System.out.println("3、入队");
System.out.println("4、出队");
System.out.println("5、查看队头数据");
System.out.println("6、输出队列的元素数量");
System.out.println("请输入你的选择:");
n = scanner.nextInt();
switch(n)
{
case 1:
queue.showQueue();
break;
case 2:
scanner.close();
loop = false;
break;
case 3:
System.out.print("请输入要入队的数据:");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 4:
try {
int res = queue.getQueue();
System.out.println("取出的数据是:"+res);
}catch(Exception e)
{
System.out.println(e.getMessage());
}
break;
case 5:
try {
int res = queue.headQueue();
System.out.println("队列头的数据为:"+res);
}catch(Exception e)
{
System.out.println(e.getMessage());
}
break;
case 6:
int l = queue.getLength();
System.out.println("队列中元素个数为:"+l);
break;
}
}
}
}