1、队列的描述
队列是一种先进先出的存储数据的结构。如我们现实生活中的排队就是一个典型的例子。
2、循环队列及其优点
2.1、循环队列是队列的扩展,就是队列首尾连接,形成一个闭环的圈子。
2.2、优点
充分利用存储空间。
3、队列的实现方式
3.1、队列与栈的实现方式一样,一般分为两种:线性队列与链式队列。
3.2、线性队列的描述
线性队列又可以分为普通队列与循环队列两种。
3.2.1、普通队列
以int值1,2,3,4,5分别进行put队列操作如下图所示
图一(普通队列)
此时,数字1,2,3,4已经将队列给占用满了,若要再put 数字5的话,需要将里面的数字全部get出来,然后再put数字5进去,如下图所示:
图二(普通队列)
3.2.2、循环队列
同样以int值1,2,3,4,5为例,分别进行put队列操作如下图所示:
图三(循环队列)
此时存储1,2,3,4是与图一的普通队列没有区别,区别在于当数据大于队列长度本身的时候,
如下所示:
图四(循环队列)
此时put数字5时与图二不一样,不需要将数据全部将数据取出,由于队列先进先出的性质,
只需get出数字1,腾出一个空间,就可以将数字5 put 队列之中了。
当然,以上的例子只是介绍什么是队列与循环队列的存储结构,
至于为什么说循环队列可以充分利用存储空间,比普通队列节省,请往下看。
3.3、线性循环队列的优势展现
对于3.2,3.3的描述,有读者会说,可以对队列的存储空间进行扩容,反正数组的索引都是固定的,操作起来效率也是蛮快的,不影响啥的。
但是无论我们将存储空间扩展的有多么的大,都是有限的,毕竟整个计算机都是有限的东西组装成的。再者研究数据结构与算法就是为了将空间与时间怎样进行到最优化,以此来节省有限的资源(包含时间)。
所以在此假设队列的长度为固定值4,不允许扩容的情况下,同样以1,2,3,4,5数字为例,如下所示:
当讲1,2,3,4数字分别依次put到队列,并get出1,2,3数字的情况下,如下图所示:
图五(普通队列)
此时,若我们这时需要将5 put到队列时对于普通队列来说只能进行扩容,但是这样就造成对原来队列空间(数字1,2,3所腾出来)的浪费。
图六(循环队列)
同样的情况,我们的循环队列可以在不扩容的情况下将数字5 put 到队列中,从而避免空间的浪费。
3.4、链式队列
链式队列也可以分为普通链式队列(单链表结构)和循环链式队列(循环单链表结构)。
3.4.1、两种方式的描述
此处是将含数字1,2,3,4,5的节点,依次以队列的方式形成链表,如下所示。
图七(普通链式队列)
与普通链式队列不同的是尾节点(tail)永远指向头节点(header),而不是NULL。
图八(循环链式队列)
优势对比,由于链式队列是对链表的尾节点(tail)进行插入操作,头节点(header)进行删除处理,所以都是O(1)的时间效率,至于空间上,这两者对于空间的占用也几乎没有什么区别,因此,整体上来说实现哪一种都差不多。
4、代码的实现
4.1、线性队列的实现(循环队列)
package com.queue;
/**
* 用数组实现队列的操作(循环队列)
*/
public class ArrayQueue {
//队列的最大长度
private static final int SIZE = 10;
//队列的当前长度
private int length;
//当前的队列头的下标
private int header;
//队列数组
private int queue[];
/**
* 初始化队列数组
* 队列的当前长度都为-1用来判断队列是否为空!
* 当前的队列头的下标的下标为0
*/
public ArrayQueue() {
queue = new int[SIZE];
length = -1;
header = 0;
}
/**
* 添加队列数据
* @param data 数据
*/
public void addQueue(int data) throws Exception {
if(length % SIZE == header) {
throw new Exception("该队列已满!");
}
if(length == -1) {
length = header;
}
//将数据放入队列数组中
queue[length++] = data;
if(length == SIZE) {
length = 0;
}
}
/**
* 获取队列数据
*/
public int getQueue() throws Exception {
if(length == -1) {
throw new Exception("该栈为已空!");
}
//将数据放入队列数组中
int data = queue[header++];
if(header == SIZE) {
header = 0;
}
if(header % SIZE == length) {
length = -1;
}
return data;
}
/**
* 清空队列
*/
public void clearQueue() {
length = -1;
}
/**
* 判断队列是否为空!
*/
public boolean isEmpty() {
return length == -1? true : false;
}
/**
* 判断队列是否已满!
*/
public boolean isFull() {
return length % SIZE == header? true : false;
}
/**
* 打印队列
*/
public void printQueue() {
if(length == -1) {
System.out.println("该队列为空!");
}
for(int i = header; (i % SIZE)< length; i++) {
System.out.print(queue[i] + " ");
}
System.out.println();
}
}
4.2、链式队列(也是循环列表为例)
4.2.1、链表节点的实现
package com.node;
/**
* 单链表节点
*
*/
public class Node {
//数据
public int data;
//指向下一个节点的引用
public Node next;
/**
* 初始化节点数据
* @param data 数据
*/
public Node(int data) {
this.data = data;
}
}
4.2.2、链式队列的实现
package com.queue;
import com.node.Node;
/**
* 使用链式实现队列的操作(循环队列)
*/
public class LinkQueue {
//队列的头节点
private Node header = null;
//队列的尾节点
private Node tail = null;
public void putQueue(Node node) {
if(header == null) {//队列不存在时(头节点就是尾节点,对尾节点进行插入操作)
header = node;
tail = node;
tail.next = header;
} else {//队列存在时(对尾节点进行插入操作)
tail.next = node;
tail = tail.next;
tail.next = header;
}
}
public int getQueue() throws Exception {
if(header == null) {
throw new Exception("该队列为已空!");
}
int data = header.data;//取出头节点的值
if(header.next != header) {//删除头节点
header = header.next;
tail.next = header;
} else {
//若头节点与尾节点同指向一个节点时,
// 此时取出该节点的值,并将头结点与尾节点置为空!
header = null;
tail = null;
}
return data;
}
/**
* 清空队列
*/
public void clearQueue() {
header = null;
tail = null;
}
/**
* 判断队列是否为空!
*/
public boolean isEmpty() {
return header == null? true : false;
}
}
5、测试
5.1、线性队列测试的代码实现
package com.test;
import com.queue.ArrayQueue;
/**
* 线性循环队列的测试
*/
public class ArrayQueueTest {
public static void main(String[] args) throws Exception{
ArrayQueue arrayQueue = new ArrayQueue();
//put值
for (int i = 0; i < 10; i++) {
arrayQueue.addQueue(i);
}
//get值
for (int i = 0; i < 10; i++) {
System.out.print(arrayQueue.getQueue() + " ");
}
System.out.println();
}
}
测试结果
5.2、链式队列测试的代码实现
package com.test;
import com.node.Node;
import com.queue.LinkQueue;
/**
* 链式循环队列的测试
*/
public class LinkQueueTest {
public static void main(String[] args) throws Exception {
LinkQueue linkQueue = new LinkQueue();
//put含数字1,2的节点
linkQueue.putQueue(new Node(1));
linkQueue.putQueue(new Node(2));
//get节点中的值
System.out.println(linkQueue.getQueue());
System.out.println(linkQueue.getQueue());
// System.out.println(linkQueue.getQueue());
}
}
测试结果
6、结束