6.1 队列的特点及抽象数据结构
队列(queue)简称队,也是一种受限的线性表。只允许在线性表的一端进行插入,在表的另一端进行删除。插入数据的一端称为队尾(rear),删除数据的一端称为队首(front)。
向队列添加数据称为入队或进队,新入队的元素称为队尾元素。从队列中删除元素称为出队或离队,元素出队之后,它的后继元素称为新的队首元素。
队列是一种先进先出(First In First Out,简称FIFO)表。
队列的抽象数据类型的定义:
ADT Queue{
数据对象:D={a0,a1,a2,...,ai} #都是同一数据类型的元素
数据关系:R={<ai,ai+1>}
数据操作:
getSise(); #返回元素个数
isEmpty(); #判断队列是否为空
enQueue(e); #入队
deQueue(); #出队
peek(); #返回队首的元素
}ADT Queue
6.2 队列的顺序存储实现
在队列的实现中,可以把数组设想称一个圆环,这种数组称为循环数组,用循环数组实现的队列称为循环队列。
用front指针指向队首元素所在的单元,使用rear指针指向队尾所在单元的后一个单元。
在元素入队时,将新入队的单元保存到rear所在的单元,然后rear指针后移。在出队时,将队首指针front指向的元素返回,然后front指针后移。
队列为空:
出队:队首指针后移
入队:队首指针后移
队列满:队首队尾也是指向同一个单元
-
如何表示队列为空还是满?
-
一般情况下,采用两种方式表示队列已满
-
少用一个存储单元,当队尾指针的下一个单元是队首指针的时,停止入队,(rear+1) % 容量 == front时表示队列满,当front == rear时,队列为空。
-
增设一个标志表示队列为空还是已满,通常用size变量表示元素的个数,当size == 0时,队列为空,当size == 容量 时,表示队列已满。
-
-
代码实现
/**
* 队列的顺序存储
*/
public class MyArrayQueue {
private Object[] elements; //定义数组存储队列中的元素
private static final int DEFAULT_LENGTH = 8;
private int front; //队首
private int rear; //队尾
private int size; //元素个数
//构造方法,指定数组大小
public MyArrayQueue(){
elements = new Object[DEFAULT_LENGTH];
}
public MyArrayQueue(int initialLength){
elements = new Object[initialLength];
}
//返回元素个数
public int getSize(){
return size;
}
//判断队列是否为空
public boolean isEmpty(){
return size == 0;
}
//入队
public void enQueue(Object e){
//如果队列已满,对数组扩容
if(size >= elements.length){
expandQueue();
}
elements[rear] = e; //把元素存储到rear指针指向的单元
rear = (rear + 1) % elements.length; //rear指针后移
size++; //元素个数+1
}
//队列的数组扩容
private void expandQueue() {
//定义一个更大的数组
Object[] newElements = new Object[elements.length * 2]; //两倍扩容
//把原来的数组复制到新的数组中,从队首开始的元素依次开始复制新数组
for(int i = 0;i<elements.length;i++){
newElements[i] = elements[front]; //新数组0位置的元素应该等于element队首位置的元素
front = (front + 1) % elements.length;
}
//把原来的数组名指向新的数组
elements = newElements;
//调整新的队首和队尾指针
front = 0;
rear = size;
}
//出队
public Object deQueue(){
//队列为空
if(size <= 0){
//抛出一个队列为空的异常
throw new QueueEmptyException("队列为空"); //自定义异常
}
//队列不为空,返回front指向的元素返回,front指针后移
Object old = elements[front];
front = (front + 1) % elements.length;
size--;
return old;
}
//返回队首元素
public Object peek(){
//队列为空
if(size <= 0){
//抛出一个队列为空的异常
throw new QueueEmptyException("队列为空");
}
return elements[front];
}
}
自定义异常
/**
* 自定义队列为空的异常
* 该异常是一个运行时异常,不需要开发人员预处理
* RuntimeException 的子类就是运行时异常
*/
public class QueueEmptyException extends RuntimeException{
public QueueEmptyException() {
}
//String参数,传递的就是异常信息
public QueueEmptyException(String message) {
super(message);
}
}
代码测试
public class QueueTest {
public static void main(String[] args) {
MyArrayQueue queue = new MyArrayQueue();
//入队
queue.enQueue("a");
queue.enQueue("b");
queue.enQueue("c");
queue.enQueue("d");
//返回队首元素
System.out.println(queue.peek()); //a
//出队
System.out.println(queue.deQueue()); // a
System.out.println(queue.deQueue()); // b
System.out.println(queue.deQueue()); // c
System.out.println(queue.deQueue()); // d
//System.out.println(queue.deQueue()); //自定义异常:Exception in thread "main" cn.xue.queue.QueueEmptyException: 队列为空
queue.enQueue("1");
queue.enQueue("2");
queue.enQueue("3");
queue.enQueue("4");
queue.enQueue("5");
queue.enQueue("6");
queue.enQueue("7");
queue.enQueue("8");
queue.enQueue("9");
queue.enQueue("10");
queue.enQueue("J");
queue.enQueue("Q");
queue.enQueue("K");
queue.enQueue("joker");
//返回队首元素
System.out.println(queue.peek()); //1
}
}
6.3 队列的链式存储实现
使用单向链表实现队列
把链表的头部作为队首,链表的尾部作为队尾。
代码实现
/**
* 队列的链式存储
*/
public class MyLinkQueue {
private Node front; //队首
private Node rear; //队尾
private int size; //元素的个数
//返回元素的个数
public int getSize(){
return size;
}
//判断队列是否为空
public boolean isEmpty(){
return size == 0;
}
//入队
public void enQueue(Object e){
//根据添加的元素生成一个结点
Node newNode = new Node(e,null);
//把结点连接到队列中
if(rear == null){ //队列为空,新结点既是头结点,又是尾结点
rear = newNode;
front = newNode;
}else{
//把结点连接到队列的尾部
rear.next = newNode;
rear = newNode; //rear指针指向新添加的元素
}
}
//出队
public Object deQueue(){
//队列为空
if(size <= 0){
//抛出一个队列为空的异常
throw new QueueEmptyException("队列为空"); //自定义异常
}
Object old = front.element; //保存头结点的元素
front = front.next; //调整队首指针,队首指针下移
//如果出队后,队列为空,调整队尾指针
if(front == null){
rear = null;
}
size--;
return old;
}
//返回队首元素
public Object peek(){
//队列为空
if(size <= 0){
//抛出一个队列为空的异常
throw new QueueEmptyException("队列为空"); //自定义异常
}
return front.element;
}
//通过内部类表示单项链表的结点
private class Node{
Object element;
Node next;
public Node(Object element,Node next){
this.element = element;
this.next = next;
}
}
}