世上有两种最耀眼的光芒,一种是太阳,一种是我们努力的模样。
什么是队列?
在日常生活中队列很常见,像我们经常排队购物或购票,排队是体现了“先来先服务”的原则。 队列在计算机系统中的应用也非常广 泛。例如: 操作系统中的作业排队。在多道程序运行的计算机系统中,可以同时有多个作业运行,它们的运算结果都需要通过通道输出,若通道尚未完成输出,则后来的作业应排队等待,每当通道完成输出时,则从队列的队头退出作业进行输出操作,而凡是申请该通道输出的作业都从队尾进人该队列等待。
队列(Queue)是另一种限定性线性表,它只允许插人在表的一端进行, 而删除在表的另一端进行,我们将这种数据结构称为队或队列,把允许插人的一端叫队尾( rear),把允许删除的一端叫队头 ( front)。队列的插人操作通常称为人队列或进队列,而队列的删除操作则称为出队列或退队列。当队列中无数据元素时,称为空队列。根据队列的定义可知,队头元素总是最先进队列的,也总是最先出队列;队尾元素总是最后进队列,因而也是最后出队列。这种表是按照先进先出(FIFO,first in first out )的原则组织数据的,因此,队列也被称为“先进先出”表。这与
我们日常生活中的排队是一致的。
队列的存储结构
与线性表、栈类似,队列也有顺序存储和链式存储两种存储方法。
(一)顺序队列
队列的顺序存储结构可以简称为顺序队列,也就是利用-组地址连续的存储单元依次存 放队列中的数据元素。一般情况下,我们使用一维数组来作为队列的顺序存储空间,另外再设立两个指示器:一个为指向队头元素位置的指示器front,另个为指向队尾的元素位置的指示器rear。
C语言实现顺序队列
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 5
#define dataType int
typedef struct{
dataType data[MAXSIZE];
int rear;
int front;
}CSeQueue;
//置空
CSeQueue* Init_CSeQueue(){
CSeQueue *queue = (CSeQueue *) malloc(sizeof(CSeQueue));
if (queue == NULL) {
return 0;
}
queue->rear = 0;
queue->front = 0;
return queue;
}
//非空判断
int IsEmpty_CSeQueue(CSeQueue *queue) {
if (queue->front == queue->rear) {
return 1;
} else {
return 0;
}
}
//判满
int IsFull_CSeQueue(CSeQueue *queue){
if (queue->rear == MAXSIZE) {
return 1;
}
return 0;
}
//入队
int In_CSeQueue(CSeQueue *queue, dataType data) {
if (IsFull_CSeQueue(queue)) {
return 0;
}
queue->data[queue->rear] = data;
queue->rear++;
return 1;
}
//出队
int Out_CSeQueue(CSeQueue *queue, dataType *data) {
if (IsEmpty_CSeQueue(queue)) {
return 0;
}
*data = queue->data[queue->front];
queue->front++;
return 1;
}
存在的问题:
建立的空队及入队、出队的示意图如上,设MAXSIZE=5。从图中可以看到,随着
入队、出队的进行,会使整个队列整体向后移动,就会出现队尾指针一静一动到了最后,再有元素入队就会出现“假溢出”,而事实上此时队中并未真的"满员",这是由于“队尾入、队头出”这种受限制的操作所造成的。
循环队列
解决假溢出的方法之一是将 队列的数据区data[0…MAXSIZE-1]看成头、尾相接的循环结构,即规定最后一个单元的后继为第一个单元, 这样整个数据区就像 一个环,我们形象地称之为循环队列。头、尾指针的关系不变,即头指示器front总是指向队列中实际队头元素的前面一个位置,而尾指示器rear总是指向队尾元素。
C语言实现循环队列
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 6
#define dataType int
typedef struct{
dataType data[MAXSIZE];
int rear;
int front;
}CSeQueue;
//置空
CSeQueue* Init_CSeQueue(){
CSeQueue *queue = (CSeQueue *) malloc(sizeof(CSeQueue));
if (queue == NULL) {
return 0;
}
queue->rear = 0;
queue->front = 0;
return queue;
}
//非空判断
int IsEmpty_CSeQueue(CSeQueue *queue) {
if (queue->front == queue->rear) {
return 1;
} else {
return 0;
}
}
//判满
int IsFull_CSeQueue(CSeQueue *queue){
if ((queue->rear + 1)%MAXSIZE == queue->front) { //浪费一个空间,用于区分队空和队满
return 1;
} else {
return 0;
}
}
//入队
int In_CSeQueue(CSeQueue *queue, dataType data) {
if (IsFull_CSeQueue(queue)) {
return 0;
}
queue->data[queue->rear] = data;
queue->rear = (queue->rear + 1) % MAXSIZE;
return 1;
}
//出队
int Out_CSeQueue(CSeQueue *queue, dataType *data) {
if (IsEmpty_CSeQueue(queue)) {
return 0;
}
*data = queue->data[queue->front];
queue->front = (queue->front + 1 ) % MAXSIZE;
return 1;
}
Java实现循环队列
package cn.boom.queue;
import java.util.Arrays;
public class SeqQueue<T> implements Queue<T> {
private T data[];
private int front;
private int rear;
private int size;
public SeqQueue() {
this.data = (T[]) new Object[11];
this.front = 0;
this.rear = 0;
this.size = 0;
}
public SeqQueue(int capacity) {
this.data = (T[]) new Object[capacity + 1];
this.front = 0;
this.rear = 0;
this.size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void enQueue(T e) {
if ((rear + 1) % getCapacity() == front) { //队满
updateCapacity(data.length * 2);
}
data[rear] = e;
this.rear = (this.rear + 1) % getCapacity();
size++;
}
@Override
public T deQueue() {
if (isEmpty()) {
throw new IllegalArgumentException("The Queue is null !");
}
T elem = data[front];
this.front = (this.front + 1) % getCapacity();
size--;
if (size < getCapacity() / 2) {
updateCapacity(getCapacity() / 2);
}
return elem;
}
@Override
public T getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("The Queue is null !");
}
return data[front];
}
/**
* 动态扩容缩容
* @param newCapacity
*/
private void updateCapacity(int newCapacity) {
T[] newData = (T[]) new Object[newCapacity];
for (int i = 0; i < this.size; i++) {
newData[i] = this.data[(i + front) % getCapacity()];
}
this.data = newData;
this.front = 0; //
this.rear = size;
}
/**
* 获取容量
* @return
*/
private int getCapacity(){
return data.length;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("SeqQueue{ data = [");
for (int i = front; i != rear; i = (i + 1) % getCapacity()) {
if ((i + 1) % getCapacity() != rear) {
res.append(data[i] + ", ");
} else {
res.append(data[i]);
}
}
res.append("]");
res.append(", front" + front);
res.append(", rear=" + rear);
res.append(", size=" + size);
res.append(", capacity=" + (getCapacity() - 1));
res.append("}");
return res.toString();
}
}
(二)链队列
循环队列虽然解决了"假溢出"问题,但若用户无法预计所需队列的最大空间,我们就只能采用链式结构来存储队列。链式存储的队列称为链队列。
和链栈类似,可以用单链表来实现链队列,根据队列的FIFO原则,链队列中为了操作方便,可以采用带头结点的单链表表示队列,并设置一个队头指针(front)和一个为指针(rear)。
C语言实现链队列
#include <stdio.h>
#include <stdlib.h>
#define dataType int
typedef struct QueueNode {
dataType data;
struct QueueNode *next;
} Node;
typedef struct {
Node *front;
Node *rear;
} slQueue;
//置空
slQueue *Init_slQueue() {
slQueue *queue = (slQueue *) malloc(sizeof(slQueue));
queue->front = (Node *) malloc(sizeof(Node));//初始化头节点
queue->rear = queue->front;
return queue;
}
//判空
int IsEmpty_slQueue(slQueue *queue) {
if (queue->front == queue->rear) {
return 1;
}
return 0;
}
//入队 : rear队尾入队
int In_slQueue(slQueue *queue, dataType data) {
Node *node = (Node *) malloc(sizeof(Node));
if (node == NULL) {
return 0;
}
node->data = data;
node->next = NULL;
queue->rear->next = node;
queue->rear = node;
return 1;
}
//出队 头结点后做队首
int Out_slQueue(slQueue *queue, dataType *data) {
if (IsEmpty_slQueue(queue)) {
return 0;
}
*data = queue->front->next->data;
if (queue->front->next == queue->rear) {
queue->rear = queue->front;
queue->front->next = queue->front->next->next;
} else {
queue->front->next = queue->front->next->next;
}
return 1;
}
Java实现链队列
复用线性表中的单链表LinkedList,很方便的实现了链队列。
数据结构:线性数据结构
package cn.boom.queue;
public class LinkedListQueue<T> implements Queue<T> {
private LinkedList<T> linkedList;
public LinkedListQueue(){
linkedList = new LinkedList<T>();
}
@Override
public int getSize() {
return linkedList.getSize();
}
@Override
public boolean isEmpty() {
return linkedList.isEmpty();
}
@Override
public void enQueue(T e) {
linkedList.addLast(e);
}
@Override
public T deQueue() {
return linkedList.remove(0);
}
@Override
public T getFront() {
return linkedList.get(0);
}
@Override
public String toString() {
return "LinkedListQueue{" +
"linkedList=" + linkedList +
'}';
}
}
参考文献
[1]王曙燕.数据结构与算法:人民邮电出版社