队列
队列,和栈一样,也是一种对数据的存和取都有严格要求的线性存储结构。
与栈结构不同的是,队列要求数据先进先出,即一端进,另一端出。
队列的特点
队列只能从表的一端存储数据,另一端取数据。
在队列中,无论是存数据还是取数据,都必须遵循“先进先出(First in first out)”的原则,即最先进队列的最先出队列。
队列的操作
基于队列结构的特点,在实际应用中,通常只会对队列执行以下两种操作:
-
向队列中添加元素,此过程被称为
入队,入队的一侧也称为队尾rear; -
从队列中提取出指定元素,此过程被称为
出队,出队的一侧也称为队头front;
队列的应用
- 生活中的排队,先到先得
- ...
队列的实现
队列存储结构的实现有以下两种方式:
- 顺序队列:在顺序表的基础上实现的队列结构;
- 链队列:在链表的基础上实现的队列结构;
两者的区别仅是顺序表和链表的区别,即在实际的物理空间中,数据集中存储的队列是顺序队列,分散存储的队列是链队列。
队列的具体实现
顺序队列
顺序队列的结构如下:
typedef int Status;
typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */
typedef struct Queue{
SElemType data[MAXSIZE];
int front; //队头
int rear; //队尾
} Queue, *QueuePtr;
- 初始化
//初始化
Status InitQueue(QueuePtr q) {
q->front = 0;
q->rear = 0;
return OK;
}
- 入队
Status InQueue(QueuePtr q, SElemType e) {
if(((q->rear + 1) % MAXSIZE) == q->front) return printError("当前队列已满", ERROR);
q->data[q->rear] = e;
q->rear = (q->rear + 1) % MAXSIZE;
return OK;
}
- 出队
Status OutQueue(QueuePtr q, SElemType *e) {
if(q->front == q->rear) return printError("当前队列为空", ERROR);
*e = q->data[q->front];
q->front = (q->front + 1) % MAXSIZE;
return OK;
}
需要注意的是
因为顺序存储是连续的存储空间,在队列的出队以及入队的过程中,front和rear指针的不停后移,会出现这样一种情况:
顺序队列整体后移造成的影响是:
- 顺序队列之前的数组存储空间将无法再被使用,造成了空间浪费;
- 如果顺序表申请的空间不足够大,则直接造成程序中数组 a 溢出,产生溢出错误;
为了解决这个问题,提出了循环队列的概念。当rear到达顺序表的末尾时,(rear+1)%MAXSIZE可以到达顺序表的开头,假如顺序表中还有空间,我们可以继续进行存储。其实就是人为的将顺序表收尾相连,形成了一个环。为了确定队列已满,我们牺牲一位空间来存放rear,当(rear+1)%MAXSIZE==front时,就认为队列已满。
rear到达表尾时的入队:
(rear+1)%MAXSIZE==front
- 获取对头元素
Status FrontQueue(Queue q, SElemType *e) {
if(q.front == q.rear) return printError("当前队列为空", ERROR);
*e = q.data[q.front];
return OK;
}
- 获取队尾元素
Status RearQueue(Queue q, SElemType *e) {
if(q.front == q.rear) return printError("当前队列为空", ERROR);
*e = q.data[(q.rear - 1 + MAXSIZE) % MAXSIZE];
return OK;
}
- 遍历队列
Status TraverseQueue(Queue q) {
if(q.front == q.rear) return printError("当前队列为空", ERROR);
int i = q.front;
int j = q.rear;
int count = (q.rear + MAXSIZE - q.front) % MAXSIZE;
printf("当前队列的元素有%d个,分别为:", count);
while(i != j) {
printf("%3d", q.data[i]);
i = (i + 1) % MAXSIZE;
}
printf("\n");
return OK;
}
- 队列置空
Status EmptyQueue(QueuePtr q) {
q->front = q->rear = 0;
return OK;
}
- 测试
MAXSIZE 10;
int main(int argc, const char * argv[]) {
Queue q;
InitQueue(&q);
for(int i = 0; i < 9; i++) {
InQueue(&q, i);
}
// EmptyQueue(&q);
TraverseQueue(q);
InQueue(&q, 9);
SElemType e;
FrontQueue(q, &e);
printf("队列头元素为%d\n", e);
RearQueue(q, &e);
printf("队列尾元素为%d\n", e);
if(OutQueue(&q, &e)) printf("出队元素%d\n", e);
TraverseQueue(q);
for(int i = 0; i < 9; i++) {
if(OutQueue(&q, &e)) printf("出队元素%d\n", e);
}
return 0;
}
当前队列的元素有9个,分别为: 0 1 2 3 4 5 6 7 8
当前队列已满
队列头元素为0
队列尾元素为8
出队元素0
当前队列的元素有8个,分别为: 1 2 3 4 5 6 7 8
出队元素1
出队元素2
出队元素3
出队元素4
出队元素5
出队元素6
出队元素7
出队元素8
当前队列为空
Program ended with exit code: 0
链式队列
链式队列就是以链式存储结构实现的队列,因为链式队列不需要连续的空间,因此理论上来说不会出现满队列。
链式队列的结构如下:
#define MAXSIZE 10 /* 存储空间初始分配量 */
typedef int Status;
typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */
//结点
typedef struct QNode{
SElemType data;
struct QNode* next;
}QNode,*QNodePtr;
//队列
typedef struct {
QNodePtr front;
QNodePtr rear;
} LinkQueue;
- 初始化
头结点,front和rear指针指向头结点。头结点的概念和之前的链表是一样的,没有什么具体的含义,头结点的next指针指向链表的第一个结点。
Status InitQueue(LinkQueue* q) {
QNodePtr node = (QNodePtr)malloc(sizeof(struct QNode));
if(node == NULL) return printError("初始化失败", ERROR);
node->data = 0;
node->next = NULL;
q->front = q->rear = node;
return OK;
}
- 入队
rear的next指向新结点,rear后移一位。
Status InQueue(LinkQueue* q, SElemType e) {
QNodePtr node = (QNodePtr)malloc(sizeof(struct QNode));
if(node == NULL) return printError("初始化失败", ERROR);
node->data = e;
node->next = NULL;
q->rear->next = node;
q->rear = node;
return OK;
}
- 出队
Status OutQueue(LinkQueue* q, SElemType *e) {
if(q->front == q->rear) return printError("队列为空", ERROR);
QNodePtr p = q->front->next;
*e = p->data;
q->front->next = p->next;
if(q->rear == p) {
q->rear = q->front;
}
free(p);
return OK;
}
- 队首元素
Status FrontQueue(LinkQueue q, SElemType *e) {
if(q.front == q.rear) return printError("队列为空", ERROR);
*e = q.front->next->data;
return OK;
}
- 队尾元素
Status RearQueue(LinkQueue q, SElemType *e) {
if(q.front == q.rear) return printError("队列为空", ERROR);
*e = q.rear->data;
return OK;
}
- 遍历队列
Status TraverseQueue(LinkQueue q) {
printf("队列的元素为:");
QNodePtr p = q.front->next;
while(p) {
printf("%3d", p->data);
p = p->next;
}
printf("\n");
return OK;
}
- 清空队列
Status EmptyQueue(LinkQueue* q) {
while(q->front->next) {
q->rear = q->front->next;
q->front->next = q->rear->next;
free(q->rear);
}
q->rear = q->front;
return OK;
}
- 销毁队列
Status DestoryQueue(LinkQueue *q) {
while(q->front) {
q->rear = q->front->next;
free(q->front);
q->front = q->rear;
}
return OK;
}
- 测试代码
int main(int argc, const char * argv[]) {
LinkQueue q;
//初始化
InitQueue(&q);
//入队
for(int i = 0; i < 9; i++) {
InQueue(&q, i);
}
// EmptyQueue(&q);
//遍历
TraverseQueue(q);
//入队
InQueue(&q, 9);
SElemType e;
FrontQueue(q, &e);
printf("队列头元素为%d\n", e);
RearQueue(q, &e);
printf("队列尾元素为%d\n", e);
if(OutQueue(&q, &e)) printf("出队元素%d\n", e);
TraverseQueue(q);
EmptyQueue(&q);
TraverseQueue(q);
DestoryQueue(&q);
return 0;
}
队列的元素为: 0 1 2 3 4 5 6 7 8
队列头元素为0
队列尾元素为9
出队元素0
队列的元素为: 1 2 3 4 5 6 7 8 9
队列为空
Program ended with exit code: 0