- 语言:C语言
- 软件:Visual Studio 2022
- 笔记书籍:数据结构——用C语言描述
- 如有错误,感谢指正。若有侵权请联系博主
一、队列的定义
队列是另一种限定性线性表,它只允许在表的一端插入元素,而在表的另一端删除元素,所以队列具有先进先出的特性。这与我们日常生活中的排队是一样的,最早进入队列的人最先离开,新来的人总是加入队尾。在队列中,允许插入的一端称为队尾,允许删除的一端则称为队头。
下面给出队列的抽象数据定义:
ADT Queue{
数据对象:可以是任意类型的数据,但必须性质相同。
结构关系:队列中数据元素之间是线性关系。
基本操作:
1、InitQueue(Q)
操作前提:Q为未初始化的队列。
操作结果:将Q初始化为一个空队列。
2、IsEmpty(Q)
操作前提:队列Q已经存在。
操作结果:若队列为空,则返回TRUE,否则返回FALSE。
3、IsFull(Q)
操作前提:队列Q已经存在。
操作结果:若队列为满,则返回TRUE,否则返回FALSE。
4、EnterQueue(Q,x)
操作前提:队列Q已经存在。
操作结果:在队列Q的队尾插入x。操作成功,返回值为TRUE,否则返回值为FALSE。
5、DeleteQueue(Q,x)
操作前提:队列Q已经存在。
操作结果:将队列Q的队头元素出队,并用x带回其值。操作成功,返回值为TRUE,否则返回值为FALSE。
6、GetHeat(Q,x)
操作前提:队列Q已经存在。
操作结果:取队列Q的对头元素(该元素不出队),并用x带回其值。操作成功,返回值为TRUE,否则返回值为FALSE。
7、ClearQueue(Q)
操作前提:队列Q已经存在。
操作结果:将队列Q置为空队列。
}ADT Queue;
二、链队的表示和实现
1、链队列结点结构
说明:为了操作方便采用带头节点的链表结构,并设置一个队头指针font和一个队尾指针rear,对头指针始终指向头节点,队尾指针指向最后一个元素。对于空的链队列,队头指针和队尾指针均指向头结点。

链队列
//结点的结构
typedef struct Node
{
QueueElementType data;
struct Node* next;
}LinkQueueNode;
//头指针尾指针结构
typedef struct
{
LinkQueueNode* front;
LinkQueueNode* rear;
}LinkQueue;
2、InitQueue初始化队列
/*初始化队列*/
int InitQueue(LinkQueue *Q) {
Q->front = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
if (Q->front != NULL) {
Q->rear = Q->front;
Q->front->next = NULL;
return (TRUE);
}
else return (FALSE);
}
说明:
第三行:开辟一个新的结构体空间(头结点),并将头节点赋值给头指针。
第四行:判断开辟的头结点是否为空,这里是考虑到有可能空间满了,没法再分配内存了。
第五、六、七行:全是满足条件执行的,先将队头指针指向的地址赋值给队尾指针指向的地址,即空链队的队头指针和队尾指针均指向头结点。再将创建的头结点的指针域置空。最后一个是返回结果,结束函数。
第九行:开辟的空间为空,即没开辟空间,返回FALSE结束循环。
3、IsEmpty判断队列是否为空
/*队列判空*/
int IsEmpty(LinkQueue* Q) {
if (Q->front->next == NULL) return (TRUE);
return (FALSE);
}
说明:
第三行:对头结点的指针域是否为空进行判断,若为空则队列为空返回 TRUE,否则返回FALSE。这一行也可以改为判断对头指针和队尾指针是否都指向同一个结点来判断。后边出队操作中用的第二个方法。
4、IsFull队列判满
链的特性,只要内存足够可以一直往队列里存储数据
5、EnterQueue链队列入队操作
/*链队列入队操作*/
int EnterQueue(LinkQueue* Q, QueueElementType x) {
LinkQueueNode* r = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
if (r == NULL) return (FALSE);
r->data = x;
r->next = NULL;
Q->rear->next = r;
Q->rear = r;
return (TRUE);
}
说明:
第三行:声明一个结点指针,并为该结点指针开辟空间赋值。
第四行:判断是否开辟成功,若没开辟空间,则r就为空,结束函数,否则执行后续代码。
第五行:将参数x的数据赋值给开辟的结点中的数据域。
第六行:将指针域置空。
第七行:将新结点赋值给队尾指针所指向的结点的指针域。
第八行:将新结点地址赋值给队尾指针。
最后返回 TRUE,表示入队操作成功。
6、 DeleteQueue链队列出队操作
/*链队列出队操作*/
int DeleteQueue(LinkQueue* Q, QueueElementType* x) {
if (Q->front == Q->rear) return (FALSE);
LinkQueueNode* r;
r = Q->front->next;
Q->front->next = r->next;
if (Q->rear == r)
Q->rear = Q->front;
*x = r->data;
free(r);
return (TRUE);
}
说明:
第三行:最开始判断队列是否为空队列,若为空,直接返回FALSE,结束函数。
第四行:声明一个结点类型指针,用于存储出队的结点。
第五行:将头指针指向的头结点的指针域地址赋值给上一行声明的r指针。
第六行:将需要取出的结点的指针域数据赋值给头结点(Q->front)的指针域。即更新了头结点指针域所指向的地址。至此,将队列中队头数据取出,赋值在r中。
第七、八行:判断取出的这个结点是否为最后一个结点,若是最后一个结点,则需要即将队尾指针指向头节点。
第九行:将取出的结点中的数据取出赋值给参数x,这里必须用指针,函数形参和实参是值传递;
第十行:用free函数释放该该结点空间,即数据取出了销毁存储数据的空间,防止内存泄漏。
第十一行:返回TRUE,函数结束。
7、GetHead取队头操作
/*队列取对头元素*/
int GetHead(LinkQueue* Q, QueueElementType* x) {
if (Q->front == Q->rear) return (FALSE);
*x = Q->front->next->data;
return (TRUE);
}
说明:
第三行:判断是否为空队列,若为空,返回FALSE直接结束函数。
第四行:将头首元结点的数据值赋值给x。Q->front是头结点,头结点的指针域(即Q->front->next)指向的是首元结点。
第五行:返回TRUE 函数结束。
8、ClearQueue队列置空操作
/*置空队列*/
int CleaQueue(LinkQueue* Q) {
if (Q->front == Q->rear) return (FALSE);
LinkQueueNode* r;
r = Q->front->next;
Q->front->next = r->next;
while (Q->rear != r) {
free(r);
r = Q->front->next;
Q->front->next = r->next;
}
free(r);
Q->rear = Q->front;
return (TRUE);
}
说明:
第三行:判断队列是否为空,若为空返回FALSE结束函数。
第四行:声明一个结点指针用于存储需要删除的结点。
第五行:将队列的队头赋值给声明的结点r,为了初始化结点r,便于while循环内判断。
第六行:更新队头数据。
第七行:判断需删除的结点是否是队尾,若是队尾,则跳出循环,不是队尾,持续循环。
第八行:将取出的队头空间删除释放。
第九行:再将队头指针赋值给声明的结点r。
第十行:更新队头数据。
第十二行:若链队内结点全删除,只剩最后一个结点,即跳出循环执行这一行。将最后一个结点的空间释放。
第十三行:将队尾指针指向头节点。
第十四行:返回TRUE,函数结束。
9、链队列的全套代码
#include <iostream>
#include <stdio.h>
#define QueueElementType int
#define TRUE 1
#define FALSE 0
typedef struct Node
{
QueueElementType data;
struct Node* next;
}LinkQueueNode;
//对头指针与队尾指针
typedef struct
{
LinkQueueNode* front;
LinkQueueNode* rear;
}LinkQueue;
/*初始化队列*/
int InitQueue(LinkQueue *Q) {
Q->front = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
if (Q->front != NULL) {
Q->rear = Q->front;
Q->front->next = NULL;
return (TRUE);
}
else return (FALSE);
}
/*队列判空*/
int IsEmpty(LinkQueue* Q) {
if (Q->front->next == NULL) return (TRUE);
return (FALSE);
}
/*链队列入队操作*/
int EnterQueue(LinkQueue* Q, QueueElementType x) {
LinkQueueNode* r = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
if (r == NULL) return (FALSE);
r->data = x;
r->next = NULL;
Q->rear->next = r;
Q->rear = r;
return (TRUE);
}
/*链队列出队操作*/
int DeleteQueue(LinkQueue* Q, QueueElementType* x) {
if (Q->front == Q->rear) return (FALSE);
LinkQueueNode* r;
r = Q->front->next;
Q->front->next = r->next;
if (Q->rear == r)
Q->rear = Q->front;
*x = r->data;
free(r);
return (TRUE);
}
/*队列取对头元素*/
int GetHead(LinkQueue* Q, QueueElementType* x) {
if (Q->front == Q->rear) return (FALSE);
*x = Q->front->next->data;
return (TRUE);
}
/*置空队列*/
int CleaQueue(LinkQueue* Q) {
if (Q->front == Q->rear) return (FALSE);
LinkQueueNode* r;
r = Q->front->next;
Q->front->next = r->next;
while (Q->rear != r) {
free(r);
r = Q->front->next;
Q->front->next = r->next;
}
free(r);
Q->rear = Q->front;
return (TRUE);
}
/*打印队列里的数据*/
int PrintLinkQueue(LinkQueue* Q) {
if (Q->front == Q->rear) return (FALSE);
LinkQueueNode* r;
r = Q->front->next;
printf("[");
while (Q->rear != r) {
printf("'%d',",r->data);
r = r->next;
}
printf("'%d']",r->data);
}
void LinkQueueText() {
LinkQueue* Q, Queue;
QueueElementType* x, data;
x = &data;
Q = &Queue;
InitQueue(Q);
printf("——————————队列插入测试——————————\n");
printf("请输入你要入队的数据,非数值结束:");
while (1 == scanf_s("%d", x)) {
EnterQueue(Q, data);
}
printf("\n队列里的数据为:");
PrintLinkQueue(Q);
printf("\n——————————队列出队测试——————————\n");
DeleteQueue(Q, x);
printf("出队元素为:%d\n", data);
DeleteQueue(Q, x);
printf("出队元素为:%d\n", data);
printf("队列剩余元素为:");
PrintLinkQueue(Q);
printf("\n——————————队列取对头元素测试——————————\n");
GetHead(Q, x);
printf("第一次取对头元素为:%d\n", data);
GetHead(Q, x);
printf("第二次取对头元素为:%d\n", data);
printf("队列剩余元素为:");
PrintLinkQueue(Q);
printf("\n——————————队列置空和队列是否为空测试——————————\n");
printf("空为1,非空为0,队列置空前:%d\n", IsEmpty(Q));
CleaQueue(Q);
printf("空为1,非空为0,队列置空后:%d", IsEmpty(Q));
}
int main() {
LinkQueueText();
}
10、测试结果
三、循环队列(顺序队列)的表示和实现
1、循环队列结点结构
循环队列是队列的一种顺序表示和实现方法。与顺序栈类似,在队列的顺序存储结构中,用一组地址连续的存储单元依次存放从队头到队尾的位置是动态变化的因此需要附设俩个指针front和rear,分别指示队头元素和队尾元素在数组中的位置。
typedef struct {
QueueElementTye element[QueueMAXSIZE];
int front;
int rear;
}SeqQueue;
第三、四行:定义一个头结点一个尾节点,用于进和出。
2、InitQueue初始化队列
/*初始化队列*/
void InitQueue(SeqQueue* Q) {
Q->front = Q->rear = 0;
}
3、IsEmpty判断队列是否为空
/*队列判空*/
int IsEmpty(SeqQueue* Q) {
if (Q->front == Q->rear) return TRUE;
return FALSE;
}
4、IsFull队列判满
/*队列判满*/
int IsFull(SeqQueue* Q) {
if ((Q->rear+1) % QueueMAXSIZE == Q->front) return TRUE;
return FALSE;
}
因为判空是头尾相连的,故没法用这个判满 再次空出一个空间 用于判断。
5、EnterQueue队尾插入
/*队尾插入*/
int EnterQueue(SeqQueue* Q, QueueElementTye x) {
if (IsFull(Q) == TRUE) return FALSE;
Q->element[Q->rear] = x;
Q->rear = (Q->rear+1)%QueueMAXSIZE;
return TRUE;
}
6、DeleteQueue队头出队
/*队头出队*/
int DeleteQueue(SeqQueue* Q, QueueElementTye* x) {
if (IsEmpty(Q) == TRUE) return FALSE;
*x = Q->element[Q->front];
Q->front = (Q->front + 1) % QueueMAXSIZE;
return TRUE;
}
7、GetHead取队头元素
/*取队头元素*/
int GetHead(SeqQueue* Q,QueueElementTye* x) {
if (IsEmpty(Q) == TRUE) return FALSE;
*x = Q->element[Q->front];
return TRUE;
}
8、ClearQueue置空队列
/*置空队列*/
void ClearQueue(SeqQueue* Q) {
memset(Q->element,NULL,sizeof(Q->element));
Q->front = Q->rear = 0;
}
9、循环队列的全套代码
#include <iostream>
#include <stdio.h>
#define QueueElementTye int
#define QueueMAXSIZE 5
#define TRUE 1
#define FALSE 0
typedef struct {
QueueElementTye element[QueueMAXSIZE];
int front;
int rear;
}SeqQueue;
/*初始化队列*/
void InitQueue(SeqQueue* Q) {
Q->front = Q->rear = 0;
}
/*队列判空*/
int IsEmpty(SeqQueue* Q) {
if (Q->front == Q->rear) return TRUE;
return FALSE;
}
/*队列判满*/
int IsFull(SeqQueue* Q) {
if ((Q->rear+1) % QueueMAXSIZE == Q->front) return TRUE;
return FALSE;
}
/*队尾插入*/
int EnterQueue(SeqQueue* Q, QueueElementTye x) {
if (IsFull(Q) == TRUE) return FALSE;
Q->element[Q->rear] = x;
Q->rear = (Q->rear+1)%QueueMAXSIZE;
return TRUE;
}
/*队头出队*/
int DeleteQueue(SeqQueue* Q, QueueElementTye* x) {
if (IsEmpty(Q) == TRUE) return FALSE;
*x = Q->element[Q->front];
Q->front = (Q->front + 1) % QueueMAXSIZE;
return TRUE;
}
/*取队头元素*/
int GetHead(SeqQueue* Q,QueueElementTye* x) {
if (IsEmpty(Q) == TRUE) return FALSE;
*x = Q->element[Q->front];
return TRUE;
}
/*置空队列*/
void ClearQueue(SeqQueue* Q) {
memset(Q->element,NULL,sizeof(Q->element));
Q->front = Q->rear = 0;
}
/*打印队列内的数据*/
void PrintSeqQueue(SeqQueue* Q) {
int i = Q->front, j = Q->rear;
while (i != j) {
printf("%d ", Q->element[i]);
i = (i + 1) % QueueMAXSIZE;
}
printf("\n");
}
void SeqQueueText() {
SeqQueue* Q, Qu;
int i;
QueueElementTye* x, data;
Q = &Qu;
x = &data;
InitQueue(Q);
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
while (1 == scanf_s("%d", &i)) {
if (i == 1) {
printf("\n请输入你要入队的数据,非数字结束");
scanf_s("%d", &data);
EnterQueue(Q, data);
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
continue;
}
if (i == 2) {
DeleteQueue(Q, x);
printf("出队的数据为:%d\n", data);
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
continue;
}
if (i == 3) {
PrintSeqQueue(Q);
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
continue;
}
if (i == 4) {
GetHead(Q, x);
printf("队头的数据为:%d\n", data);
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
continue;
}
if (i == 5) {
ClearQueue(Q);
printf("置空完成!\n");
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
continue;
}
if (i == 6) {
printf("满栈为1,非满为0:%d\n", IsFull(Q));
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
continue;
}
if (i == 7) {
printf("空栈为1,非空为0:%d\n", IsEmpty(Q));
printf("1.入队,2.出队,3.查询整个队列,4.查询队头,5.置空队列,6.为满查询,7.为空查询,q结束。请输入你想执行的操作:");
continue;
}
}
}
int main() {
SeqQueueText();
}