数据结构-队列
队列
- 队列(queue)简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。在队列中把插入数据元素的一端称为队尾(rear),删除数据元素的一端称为队首(front)。向队尾插入元素称为进队或入队,新元素入队后成为新的队尾元素;从队列中删除元素称为离队或出队,元素出队后,其后续元素成为新的队首元素。
- 由于队列的插入和删除操作分别在队尾和队首进行,每个元素必然按照进入的次序离队,也就是说先进队的元素必然先离队,所以称队列为先进先出表(First In First Out,简称 FIFO)。队列结构与日常生活中排队等候服务的模型是一致的,早进入队列的人,早得 到服务并从队首离开;后到来的人只能排在队列的后,后得到服务并后离开。
Queue 接口
代码实现如下:
package com.wjy.Data_Structure.queue;
public interface Queue {
/**
* 返回队列的大小
*
* @return
*/
public int getSize();
/**
* 判断队列是否为空
*
* @return
*/
public boolean isEmpty();
/**
* 数据元素 e 入队
*
* @param e
*/
public void enqueue(Object e);
/**
* 队首元素出队
*
* @return
*/
public Object dequeue() throws QueueEmptyException;
/**
* 取队首元素
*
* @return
*/
public Object peek() throws QueueEmptyException;
}
异常类定义
package com.wjy.Data_Structure.queue;
public class QueueEmptyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public QueueEmptyException(String err) {
super(err);
}
}
1.队列的顺序存储实现
在队列的顺序存储实现中,我们可以将队列当作一般的表用数组加以实现,但这样做的效果并不好,当我们删除队首元素时候必须将数组中所有其他元素都向前移动一个位置。为了提高运算的效率,设想数组 A[0.. capacity-1]中的单元不是排成一行,而是围成一个圆环,即 A[0]接在 A[capacity-1]的后面。这种意义下的数组称为循环数组,如图所示:
代码实现如下:
package com.wjy.Data_Structure.queue;
public class QueueArray implements Queue {
private static final int CAP = 7; // 队列默认大小
private Object[] elements; // 数据元素数组
private int capacity; // 数组的大小
private int front; // 队首指针,指向队首
private int rear; // 队尾指针,指向队尾后一个位置
public QueueArray() {
this(CAP);
}
public QueueArray(int cap) {
capacity = cap + 1;
elements = new Object[capacity];
front = rear = 0;
}
private void expandSpace() {
Object[] a = new Object[elements.length * 2];
int i = front, j = 0;
while (i != rear) {
a[j++] = elements[i];
i = (i + 1) % capacity;
}
elements = a;
capacity = elements.length;
front = 0;
rear = j;
}
@Override
public int getSize() {
return (rear - front + capacity) % capacity;
}
@Override
public boolean isEmpty() {
return front == rear;
}
@Override
public void enqueue(Object e) {
if (getSize() == capacity - 1)
expandSpace();
elements[rear] = e;
rear = (rear + 1) % capacity;
}
@Override
public Object dequeue() throws QueueEmptyException {
if (isEmpty())
throw new QueueEmptyException("错误:队列为空");
Object obj = elements[front];
elements[front] = null;
front = (front + 1) % capacity;
return obj;
}
@Override
public Object peek() throws QueueEmptyException {
if (isEmpty())
throw new QueueEmptyException("错误:队列为空");
return elements[front];
}
}
2.队列的链式存储实现
队列的链式存储可以使用单链表来实现。这里采用带头结点的单链表结构。如图所示。队首指针指向队首元素的前一个结点,即始终指向链表空的头结点,队尾指针指向队列当前队尾元素所在的结点。当队列为空时,队首指针与队尾指针均指向空的头结点。
代码实现如下:
package com.wjy.Data_Structure.queue;
import com.wjy.Data_Structure.linearlist.common.SLNode;
public class QueueSLinked implements Queue {
private SLNode front; // 队首指针
private SLNode rear; // 队尾指针
private int size; // 元素个数
public QueueSLinked() {
this.front = new SLNode();
this.rear = front;
this.size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void enqueue(Object e) {
SLNode p = new SLNode(e, null);
rear.setNext(p);
rear = p;
size++;
}
@Override
public Object dequeue() throws QueueEmptyException {
if (size < 1)
throw new QueueEmptyException("错误:队列为空");
SLNode p = front.getNext();
front.setNext(p.getNext());
size--;
if (size < 1)
rear = front; // 如果队列为空,rear指向头结点
return p.getData();
}
@Override
public Object peek() throws QueueEmptyException {
if (size < 1)
throw new QueueEmptyException("错误:队列为空");
return front.getNext().getData();
}
}