4-队列
队列是只允许在一端进行插入操作,在另一端进行删除操作的受限的线性表。
队列是一种 先进先出(FIFO) 的线性表,就像水管一样,从一头进去,从另一头出来
队列有两个指针,一个指向 队头,只允许删除;一个指向 队尾,只允许插入。
对于队列的操作有插入和删除,也称作 入队 和 出队,不能从中间取。
- 顺序队:队列的每一个元素在一段连续的内存空间,用数组实现。
- 链队:队列的每一个元素不一定紧挨着,用链表实现。
- 循环队:队头连接着队尾。
队的主要操作包括:创建、初始化、入队、出队、判空、判满、清空队列。
循环队
顺序队是基于数组实现的,当入队一个元素的时候,队尾指针+1,当队尾指针=MAX_SIZE
时队满;当出队一个元素的时候,队头指针+1,当队头指针=MAX_SIZE
时,说明队空。
但是!!
当队头和队尾都 = MAX_SIZE
时,实际上队空,但是却无法入队新元素,这种现象称之为 假溢出。
所以为了解决这个问题,我们可以用 循环队列。
要实现循环队列,最重要的是怎么做才能让队头队尾指针在一个周期中循环。
答案是 front = (front + 1) % MAX_SIZE、rear = (rear + 1) % MAX_SIZE。
假设 MAX_SIZE == 8, front == 1
,(front + 1) % MAX_SIZE
等价于 2 % 8 == 2
所以这条表达式,可以让队头队尾指针在 0~MAX_SIZE-1
这个周期中循环,也就是在 0, 1, 2, ... MAX_SIZE-1
中循环。
要素
循环队列的两个状态
队空: q->rear == q->front
队满: (q->rear + 1) % MAX_SIZE == q->front
定义
| // DataElement.h
#ifndef DATAELEMENT_H_INCLUDED
#define DATAELEMENT_H_INCLUDED
#define MAX_SIZE 10
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int Bool;
typedef struct {
int id;
char* name;
} Element;
typedef struct {
Element datas[MAX_SIZE];
int front;
int rear;
} SqQueue;
#endif // DATAELEMENT_H_INCLUDED
|
创建
| Status Create(SqQueue* q)
{
q->front = 0;
q->rear = 0;
}
|
初始化
| Status Init(SqQueue* q, Element* datas, int length)
{
if (length > MAX_SIZE) {
return ERROR;
}
if (!(q->front && q->rear))
Create(q);
for (int i = 0; i < length; i++) {
EnQueue(q, datas[i]);
}
}
|
入队 & 出队
判空
| Bool isEmpty(SqQueue* q)
{
return q->front == q->rear;
}
|
判满
| Bool isFull(SqQueue* q)
{
return q->front == (q->rear + 1) % MAX_SIZE;
}
|
打印队列
| void Print(SqQueue* q)
{
if (isEmpty(q))
printf("队列为空!");
for (int i = q->front + 1; i < q->rear + 1; i++) {
printf("%d \t %s \n", q->datas[i].id, q->datas[i].name);
}
}
|
测试数据
| // main.c
#include "SqQueue.c"
#define len(X) sizeof(X) / sizeof(X[0])
Element datas[] = {
{ 1, "Iron Man" },
{ 2, "Thor" },
{ 3, "Captain" },
{ 4, "Green Man" }
};
void main()
{
SqQueue q;
Create(&q);
Init(&q, datas, len(datas));
printf("After Init(): \n");
Print(&q);
printf("\n-------------------------------------\n");
Element el = { 5, "Big Man" };
EnQueue(&q, el);
printf("After EnQueue(): \n");
Print(&q);
printf("\n-------------------------------------\n");
DeQueue(&q, &el);
printf("After DeQueue(): \n");
printf("%d \t %s", el.id, el.name);
}
|
链队
链队相比循环队则简单多了,入队就是链表的插入,出队就是链表的删除,队空就是队头指针和队尾指针都为 NULL ,没有队满。
定义
| // DataElement.h
#ifndef DATAELEMENT_H_INCLUDED
#define DATAELEMENT_H_INCLUDED
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct {
int id;
char* name;
} Element;
typedef struct Node {
Element data;
struct Node* next;
} Node;
// 定义链队结构体
typedef struct {
Node* front;
Node* rear;
} LinkQueue;
#endif // DATAELEMENT_H_INCLUDED
|
创建
| Status Create(LinkStack* q)
{
q->front = NULL;
q->rear = NULL;
return OK;
}
|
在创建的时候,将两个队头队尾两个指针置空
初始化
| Status Init(LinkQueue* q, Element* datas, int length)
{
if (!isEmpty(q)) Create(q);
for (int i = 0; i < length; i++) {
EnQueue(q, datas[i]);
}
return OK;
}
|
入队
| Status EnQueue(LinkQueue* q, Element e)
{
Node* new = (Node*)malloc(sizeof(Node));
new->data = e;
new->next = NULL;
if (isEmpty(q)) { // 如果队列为空,除了让队尾指向新结点,让队头也指向新结点
q->front = q->rear = new;
return OK;
} else { // 如果队列不为空,让旧的队尾元素指向新结点,队尾指针指向新结点
q->rear->next = new;
q->rear = new;
return OK;
}
return ERROR;
}
|
入队就是让原本的队尾指向新的结点,这一步是让新结点加入链表;
然后让队尾指针也指向新的结点,这一步是对队尾指针的更新。
如果队列为空时,队头指针和队尾指针一起指向新结点。
出队
| Element DeQueue(LinkQueue* q, )
{
Element e;
if (isEmpty(q)) return e;
if (q->front == q->rear) { // 链队中只有一个元素时,出队后队空,所以需要恢复队头和队尾指针为空
e = q->rear->data;
free(q->rear);
q->front = q->rear = NULL;
return e;
}
// 链队中不止一个元素时
// 1. 取元素
e = q->front->data;
// 2. 改链
Node* de = q->front;
q->front = de->next;
// 3. 释放空间
free(de);
de = NULL;
return e;
}
|
出队的时候要考虑三种情况:
- 队空,则返回一个空元素;
- 只有一个元素,出队后队头指针和队尾指针都要置空;
- 不止一个元素,则取元素、改链、释放空间,最后返回元素。
判空
| bool isEmpty(LinkQueue* q)
{
return q->front == NULL && q->rear == NULL;
}
|
清空链队
| Status Clear(LinkQueue* q)
{
if (isEmpty(q)) return OK;
while (q->front) {
DeQueue(q);
}
return OK;
}
|
销毁链队
| Status Destory(LinkQueue* q)
{
Clear(q);
free(q);
return OK;
}
|
打印链队
| void Print(LinkQueue* q)
{
if (isEmpty(q)) {
printf("队列为空!");
return;
}
Node* curr = q->front;
while (curr) {
printf("%d \t %s \n", curr->data.id, curr->data.name);
curr = curr->next;
}
}
|
完整代码
顺序队、循环队
| // DataElement.h
#ifndef DATAELEMENT_H_INCLUDED
#define DATAELEMENT_H_INCLUDED
#define MAX_SIZE 10
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int Bool;
typedef struct {
int id;
char* name;
} Element;
typedef struct {
Element datas[MAX_SIZE];
int front;
int rear;
} SqQueue;
#endif // DATAELEMENT_H_INCLUDED
|
| // SqQueue.h
#ifndef DQQUEUE_H_INCLUDED
#define DQQUEUE_H_INCLUDED
#include "DataElement.h"
Status Create(SqQueue* q);
Status Init(SqQueue* q, Element* datas, int length);
Status EnQueue(SqQueue* q, Element e);
Status DeQueue(SqQueue* q, Element* e);
Bool isEmpty(SqQueue* q);
Bool isFull(SqQueue* q);
void Print(SqQueue* q);
#endif // DQQUEUE_H_INCLUDED
|
| // SqQueue.c
#include "SqQueue.h"
#include <stdio.h>
#include <stdlib.h>
Status Create(SqQueue* q)
{
q->front = 0;
q->rear = 0;
}
Status Init(SqQueue* q, Element* datas, int length)
{
if (length > MAX_SIZE) {
return ERROR;
}
if (!(q->front && q->rear))
Create(q);
for (int i = 0; i < length; i++) {
EnQueue(q, datas[i]);
}
}
Status EnQueue(SqQueue* q, Element e)
{
// 如果队满, 无法入队
if (isFull(q))
return ERROR;
q->rear = (q->rear + 1) % MAX_SIZE; // 先把队尾移动一格
q->datas[q->rear] = e; // 再把数据放进去
return OK;
}
Status DeQueue(SqQueue* q, Element* e)
{
// 如果队空,无法出队
if (isEmpty(q))
return ERROR;
q->front = (q->front + 1) % MAX_SIZE;
*e = q->datas[q->front];
return OK;
}
Bool isEmpty(SqQueue* q)
{
return q->front == q->rear;
}
Bool isFull(SqQueue* q)
{
return q->front == (q->rear + 1) % MAX_SIZE;
}
void Print(SqQueue* q)
{
if (isEmpty(q))
printf("队列为空!");
for (int i = q->front + 1; i < q->rear + 1; i++) {
printf("%d \t %s \n", q->datas[i].id, q->datas[i].name);
}
}
|
| // main.c
#include "SqQueue.c"
#define len(X) sizeof(X) / sizeof(X[0])
Element datas[] = {
{ 1, "Iron Man" },
{ 2, "Thor" },
{ 3, "Captain" },
{ 4, "Green Man" }
};
void main()
{
SqQueue q;
Create(&q);
Init(&q, datas, len(datas));
printf("After Init(): \n");
Print(&q);
printf("\n-------------------------------------\n");
Element el = { 5, "Big Man" };
EnQueue(&q, el);
printf("After EnQueue(): \n");
Print(&q);
printf("\n-------------------------------------\n");
DeQueue(&q, &el);
printf("After DeQueue(): \n");
printf("%d \t %s", el.id, el.name);
}
|
链队
| // DataElement.h
#ifndef DATAELEMENT_H_INCLUDED
#define DATAELEMENT_H_INCLUDED
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct {
int id;
char* name;
} Element;
typedef struct Node {
Element data;
struct Node* next;
} Node;
typedef struct {
Node* front;
Node* rear;
} LinkQueue;
#endif // DATAELEMENT_H_INCLUDED
|
| // LinkQueue.h
#ifndef LINKQUEUE_H_INCLUDED
#define LINKQUEUE_H_INCLUDED
#include <stdbool.h>
#include "DataElement.h"
Status Create(LinkQueue* q);
Status InitLinkQ(LinkQueue* q, Element* datas, int length);
Status EnQueue(LinkQueue* q, Element e);
Element DeQueue(LinkQueue* q);
Status Clear(LinkQueue* q);
Status Destory(LinkQueue* q);
bool isEmpty(LinkQueue* q);
void Print(LinkQueue* q);
#endif // LINKQUEUE_H_INCLUDED
|
| // LinkQueue.c
#include "LinkQueue.h"
#include <stdio.h>
#include <stdlib.h>
Status Create(LinkQueue* q)
{
q->front = NULL;
q->rear = NULL;
return OK;
}
Status InitLinkQ(LinkQueue* q, Element* datas, int length)
{
if (!isEmpty(q)) {
Create(q);
}
for (int i = 0; i < length; i++) {
EnQueue(q, datas[i]);
}
return OK;
}
Status EnQueue(LinkQueue* q, Element e)
{
Node* new = (Node*)malloc(sizeof(Node));
new->data = e;
new->next = NULL;
// 如果队空,让队头也指向新结点
if (isEmpty(q)) {
q->front = q->rear = new;
return OK;
} else {
// 队尾元素 指向新结点
q->rear->next = new;
// 队尾指针 指向新结点
q->rear = new;
return OK;
}
return ERROR;
}
Element DeQueue(LinkQueue* q)
{
Element e;
if (isEmpty(q))
return e;
// 如果队中只有一个元素
if (q->front == q->rear) {
e = q->front->data;
free(q->front);
q->front = q->rear = NULL;
return e;
}
// 1. 改链
Node* de = q->front;
q->front = de->next;
// 2. 取元素
e = de->data;
// 3. 释放空间
free(de);
de = NULL;
return e;
}
bool isEmpty(LinkQueue* q)
{
return q->front == NULL && q->rear == NULL;
}
Status Clear(LinkQueue* q)
{
if (isEmpty(q)) {
return OK;
}
while (q->front) {
DeQueue(q);
}
return OK;
}
Status Destory(LinkQueue* q)
{
Clear(q);
free(q);
return OK;
}
void Print(LinkQueue* q)
{
if (isEmpty(q)) {
printf("队列为空!");
return;
}
Node* curr = q->front;
while (curr) {
printf("%d \t %s \n", curr->data.id, curr->data.name);
curr = curr->next;
}
}
|
| // main.c
#include "LinkQueue.c"
#define len(X) sizeof(X) / sizeof(X[0])
Element datas[] = {
{ 1, "Iron Man" },
{ 2, "Thor" },
{ 3, "Captain" },
{ 4, "Green Man" }
};
void main()
{
LinkQueue q;
Create(&q);
InitLinkQ(&q, datas, len(datas));
Print(&q);
printf("\n-------------------------------------\n");
Element e = { 5, "Spider Man" };
EnQueue(&q, e);
Print(&q);
printf("\n-------------------------------------\n");
Element de = DeQueue(&q);
printf("Element deleted: %d \t %s \n", de.id, de.name);
printf("\n-------------------------------------\n");
Print(&q);
printf("\n-------------------------------------\n");
Clear(&q);
Print(&q);
printf("\n-------------------------------------\n");
// Destory(&q);
// Print(&q);
}
|