*文中内容大多来源于《数据结构 --Java语言描述》(第二版) 刘小晶 杜选 主编
*此系列文章作为学习记录,若文中内容有误,请大家指出,谢谢
队列是一种特殊的线性表,它的特殊性体现在队列值允许在表尾插入数据元素,在表头删除数据元素,所以队列也是一种操作受限的特殊的线性表,它具有先进先出(First In First Out, FIFO)或后进后出(Last In Last Out, LILO)。
允许进行插入的一端称为队尾(rear),允许进行删除的一段称为是队首(front)。
队列的抽象数据类型描述
public interface IQueue {
public void clear();
public boolean isEmpty();
public int length();
public Object peek(); //取队首元素并返回其值
public void offer(Object x) throws Exception; //入队操作
public Object poll(); //出队操作
}
顺序队列的实现
/**
* 顺序队列有可能因为多次入队和出队操作后出现有存储空间,但不能进行入队操作的溢出现象称为“假溢出”
* 要解决假溢出,最好的办法就是把顺序队列所使用的存储空间看成是一个逻辑上首尾相连的循环队列。
* 当rear或front到达maxSize - 1后,再加1就自动到0。这种转换可以利用取余运算来实现,即令rear = (rear + 1) % maxSize
*
* 但是,循环顺序队列中存在另一个问题:无法区分队空和队满的状态,这是因为循环顺序队列的判空和判满的条件都是front == rear
* 解决方法:
* (1)少用一个存储单元
* 当顺序存储空间的容量为maxSize时,只允许最多存放maxSize - 1个数据元素。此时,队空的判断条件为:front == rear,
* 而队满的判断条件为:front == (rear + 1) % maxSize。
* (2)设置一个标志变量
* 在程序设计过程中引进一个标志变量flag,其初始值为0,每当入队操作成功后就置flag = 1;每当出队操作成功后就置flag = 0,
* 则此时队空的判断条件为:front == rear && flag == 0,而队满的判断条件为:front == rear && flag == 1
* (3)设置一个计数器
* 在程序设计过程中引进一个计数变量num,其初始值为0,每当入队操作成功后就 + 1;每当出队操作成功后就 - 1,则此时队空
* 的判断条件为:num == 0,而队满的判断条件为:num > 0 && front == rear。
*/
public class CircleSqQueue implements IQueue {
private Object[] queueElem; //队列存储空间
private int front; //队首的引用,若队列不空,指向队首元素
private int rear; //队尾的引用,若队列不空,指向队尾元素
//循环队列类的构造元素
public CircleSqQueue(int maxSize){
front = rear = 0; //队首、队尾初始化为0
queueElem = new Object[maxSize]; //为队列分配maxSize个存储单元
}
//队列置空
public void clear(){
front = rear = 0;
}
//判队列是否为空
public boolean isEmpty(){
return front == rear;
}
//对队列的长度
public int length(){
return (rear - front + queueElem.length) % queueElem.length;
}
//读取队首元素
public Object peek(){
if (front == rear) //队列为空
return null;
else //返回队首元素
return queueElem[front];
}
//入队
public void offer(Object x) throws Exception{
if ((rear + 1) % queueElem.length == front) //队列满
throw new Exception("队列已满"); //抛出异常
else{
queueElem[rear] = x; //x存入rear所指的数组存储位置中,使其称为新的队尾元素
rear = (rear + 1) % queueElem.length; //修改队尾指针
}
}
//出队
public Object poll(){
if (front == rear) //队列为空
return null;
else {
Object t = queueElem[front];
front = (front + 1) % queueElem.length;
return t; //返回队列的队首元素
}
}
//输出队列中的所有数据元素(从队首到队尾)
public void display(){
if (!isEmpty()){
for (int i = front; i != rear; i = (i + 1) % queueElem.length)
System.out.print(queueElem[i].toString() + " ");
}else{
System.out.println("此队列为空");
}
}
}
链队列的实现
//队列的链式存储结构用不带头结点的单链表来实现
import Book_U2.Node;
public class LinkQueue implements IQueue{
private Node front; //队首指针
private Node rear; //队尾指针
//链队列类的构造函数
public LinkQueue(){
front = rear = null;
}
//队列置空
public void clear(){
front = rear = null;
}
//队列判空
public boolean isEmpty(){
return front == null;
}
//求队列的长度
public int length(){
Node p = front;
int length = 0;
while(p != null){
p = p.next; //指针下移
++length; //计数器加一
}
return length;
}
//取队首元素
public Object peek(){
if (front != null) //队列非空
return front.data; //返回队首结点的数据域值
else
return null;
}
//入队
public void offer(Object x){
Node p = new Node(x); //初始化新结点
if (front != null){ //队列非空
rear.next = p;
rear = p; //改变队尾的位置
}
else
front = rear = p;
}
//出队
public Object poll(){
if (front != null){ //队列非空
Node p = front; //p指向队首结点
front = front.next; //队首结点出列
if (p == rear) //被删除的结点是队尾结点时
rear = null;
return p.data; //返回队首结点的数据域值
}
else
return null;
}
}
结点类的描述
/**
* data是数据域,用来存放数据元素的值;next是指针域,用来存放后继结点的地址
*/
public class Node {
public Object data; //存放结点值
public Node next; //后继结点的引用
//无参数时的构造函数
public Node(){
this(null,null);
}
//带一个参数时的构造函数
public Node(Object data){
this(data,null);
}
//带两个参数时的构造函数
public Node(Object data,Node next){
this.data = data;
this.next = next;
}
}