🚀 写在最前:这篇文章将学习队列这种结构,以及该结构的一些基本操作的实现,包括顺序存储队列和链式存储队列的基本操作的实现。
🚀:点个关注吧😀,让我们一起探索计算机的奥秘!
一、队列的定义
队列和栈差不不多,它也是一种受限制的线性表,它受的限制是它只能它线性表的一端插入数据(即入队),只能在线性表的另外一端删除数据(即出队)。
二、队列的基本操作
InitQueue(&2)
:初始化队列,构造一个空队列Q。QueueEmpty(Q)
:判队列空,若队列Q为空返回true,否则返回false。EnQueue(&Q,x)
:入队,若队列Q未满,将x加入,使之成为新的队尾。DeQueue(&Q)
:出队,若队列Q非空,删除队头元素,并用返回出队首元素。GetHead(Q)
:读队头元素,若队列Q非空,则将返回出队首元素。
三、队列基本操作的实现
1)顺序存储
文件结构:
包含三个文件,一个SqQueue.h
文件用于定义数据结构和数据结构的基本操作,一个SqQueue.cpp
文件,该文件用于具体实现这些基本操作,一个test.cpp
用于测试实现的函数是否正确。
SqQueue.h
首先定义相应的数据结构,和相应的操作。
#pragma once
#include<stdio.h>
#include<stdlib.h>
#define maxsize 100
typedef int ElementType;
typedef struct SqQueue {
ElementType data[maxsize]; //队列的大小
int front, rear; //一个指向队列的队头,一个指向队尾
};
①队列初始化操作
SqQueue.h
//队列初始化操作
void InitQueue(SqQueue &Q);
SqQueue.cpp
//队列初始化操作
void InitQueue(SqQueue &Q) {
Q.front = Q.rear = 0; //此时队列为空
}
test.cpp
#include"SqQueue.h"
int main() {
SqQueue q;
InitQueue(q); //初始化
printf("头指针为%d,尾指针为%d\n", q.front, q.rear);
return 0;
}
②入队
SqQueue.h
//入队
bool InQueue(SqQueue& Q, ElementType e);
SqQueue.cpp
在一步中比较难理解的就是if ((Q.rear + 1) % maxsize == Q.front)
判满条件为什么是这样的。
因为当入队操作到达地址为6的空间时候,其实队列空间并没有满,还有地址为0、1的空着,此时队列地址应该指向地址空间为0的区域,而正好当队尾指向地址空间为6时候入队7,入队完成后队尾值应该++,就会变成7,而地址空间没有7 ,对7取模型运算会让其正好指向0地址空间。
此时队尾指向0,这时候有用元素8入队,队尾指针继续++;此时指向地址空间1的位置,此时认为空间是满的,即使还有地址空间为1的位置未放,任然认为是满的,所以判满条件为(Q.rear + 1) % maxsize == Q.front
。
为什么要空一个位置不放?若放的话,位置1假设入队元素9,入队后,队尾值++,此时队尾和队头相等,指向同一个空间,而刚刚初始化队列时候,即队列为空的情况是也是Q.front = Q.rear
,所以需要浪费一个存储空间来区别出队列满和空的情况。(当然也有方法不浪费空间来区分,可以自己探索一下!!)
//入队
bool InQueue(SqQueue& Q, ElementType e) {
if ((Q.rear + 1) % maxsize == Q.front) { //判断队列是否满,以空出一个存储空间为代价
printf("队列空间满导致入队失败\n");
return false;
}
else {
Q.data[Q.rear] = e; //将元素入队尾部,rear指向队尾
Q.rear = (Q.rear + 1) % maxsize; //将rear指向下一个位置
}
}
test.cpp
#include"SqQueue.h"
int main() {
SqQueue q;
InitQueue(q); //初始化
printf("头指针为%d,尾指针为%d\n", q.front, q.rear);
InQueue(q, 4);
printf("尾指针为%d,入队元素为%d\n", q.rear, q.data[q.rear - 1]);
return 0;
}
③出队
SqQueue.h
//出队
ElementType OutQueue(SqQueue& Q);
SqQueue.cpp
//出队
ElementType OutQueue(SqQueue& Q) {
if (Q.front == Q.rear) { //判断队列是否为空
printf("出队失败,队列为空\n");
return -1;
}
else {
ElementType tmp = Q.data[Q.front];
Q.front = (Q.front + 1) % maxsize;
return tmp;
}
}
test.cpp
#include"SqQueue.h"
int main() {
SqQueue q;
InitQueue(q); //初始化
printf("头指针为%d,尾指针为%d\n", q.front, q.rear);
InQueue(q, 4);
printf("尾指针为%d,入队元素为%d\n", q.rear, q.data[q.rear - 1]);
int tmp = OutQueue(q);
if (tmp != -1) {
printf("出队元素为%d\n", tmp);
}
OutQueue(q); //此时为空,测试为空的情况
return 0;
}
④读队头元素
SqQueue.h
//读队头元素
ElementType GetElement(SqQueue Q);
SqQueue.cpp
//读队头元素
ElementType GetElement(SqQueue Q) {
if (Q.front == Q.rear) {
printf("队列为空,读取对头元素失败\n");
return -1;
}
else {
return Q.data[Q.front];
}
}
test.cpp
#include"SqQueue.h"
int main() {
SqQueue q;
InitQueue(q); //初始化
printf("头指针为%d,尾指针为%d\n", q.front, q.rear);
InQueue(q, 4);
printf("尾指针为%d,入队元素为%d\n", q.rear, q.data[q.rear - 1]);
int tmp = OutQueue(q);
if (tmp != -1) {
printf("出队元素为%d\n", tmp);
}
OutQueue(q); //此时为空,测试为空的情况
GetElement(q); //此时为空,测试为空的情况
InQueue(q,99);
InQueue(q,88);
int tmp2 = GetElement(q);
if (tmp2 != -1) {
printf("队头元素为%d\n", tmp2);
}
return 0;
}
2)链式存储
文件结构:
包含三个文件,一个LinkQueue.h
文件用于定义数据结构和数据结构的基本操作,一个LinkQueue.cpp
文件,该文件用于具体实现这些基本操作,一个test.cpp
用于测试实现的函数是否正确。
①队列初始化操作
LinkQueue.h
#pragma once
#include <stdbool.h>
#include<stdlib.h>
#include<stdio.h>
typedef int ElementType;
// 定义节点
typedef struct LinkQueueNode {
ElementType data; // 节点元素
struct LinkQueueNode* next; // 指向下一个节点的指针域
} LinkQueueNode;
// 定义队列
typedef struct LinkQueue{
LinkQueueNode* front;
LinkQueueNode* rear;
}LinkQueue;
// 初始化操作
bool InitQueue(LinkQueue &LinkQ);
LinkQueue.cpp
#include"LinkQueue.h"
//初始化操作
bool InitQueue(LinkQueue &LinkQ) {
//将front 和 rear 都指向头节点
LinkQueueNode*tmp = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
LinkQ.front = LinkQ.rear = tmp;
if (tmp == NULL) {
printf("空间申请失败导致初始化失败\n");
return false;
}
else {
LinkQ.front->next = NULL;
return true;
}
}
test.cpp
#include"LinkQueue.h"
int main() {
LinkQueue LQ;
InitQueue(LQ); //初始化LQ
printf("初始化后的队列的头指针为%p,尾指针为%p\n", LQ.front, LQ.rear);
return 0;
}
②入队
LinkQueue.h
//入队操作
bool InQueue(LinkQueue& LinkQ, ElementType e);
LinkQueue.cpp
//入队操作
bool InQueue(LinkQueue& LinkQ , ElementType e) {
LinkQueueNode* tmp = (LinkQueueNode*)malloc(sizeof(LinkQueueNode));
if (tmp == NULL) {
printf("空间申请失败导致入队操作失败\n");
return false;
}
else {
tmp->data = e;
tmp->next = NULL;
LinkQ.rear->next = tmp;
LinkQ.rear = tmp; //将队尾指针指向新入队的节点
}
}
test.cpp
#include"LinkQueue.h"
int main() {
LinkQueue LQ;
InitQueue(LQ); //初始化LQ
printf("初始化后的队列的头指针为%p,尾指针为%p\n", LQ.front, LQ.rear);
InQueue(LQ, 4);
printf("尾指针为%p,入队元素为%d\n", LQ.rear, LQ.rear->data);
return 0;
}
③出队
LinkQueue.h
//出队
ElementType OutQueue(LinkQueue& LinkQ);
LinkQueue.cpp
//出队
ElementType OutQueue(LinkQueue& LinkQ) {
if (LinkQ.front->next == NULL) {
printf("队列为空,出队失败!\n");
return -1;
}
else {
ElementType tmp = LinkQ.front->next->data;
LinkQueueNode* tmp1 = LinkQ.front->next;
LinkQ.front->next = tmp1->next;
if (LinkQ.rear == tmp1) {
LinkQ.rear = LinkQ.front; //若只剩下一个节点被删除就要把两个指针都指向头节点
}
free(tmp1);
return tmp;
}
}
test.cpp
#include"LinkQueue.h"
int main() {
LinkQueue LQ;
InitQueue(LQ); //初始化LQ
printf("初始化后的队列的头指针为%p,尾指针为%p\n", LQ.front, LQ.rear);
InQueue(LQ, 4);
printf("尾指针为%p,入队元素为%d\n", LQ.rear, LQ.rear->data);
printf("出队元素为%d\n",OutQueue(LQ));
printf("头指针为%p,尾指针为%p\n", LQ.front, LQ.rear);
OutQueue(LQ); //此时无元素,测试无元素出队
return 0;
}
④读队头元素
LinkQueue.h
//读队头元素
ElementType GetElement(LinkQueue LinkQ);
LinkQueue.cpp
//读队头元素
ElementType GetElement(LinkQueue LinkQ) {
if (LinkQ.front->next == NULL) {
printf("队列为空,读对头失败!\n");
return -1;
}
else {
ElementType tmp = LinkQ.front->next->data;
return tmp;
}
}
test.cpp
#include"LinkQueue.h"
int main() {
LinkQueue LQ;
InitQueue(LQ); //初始化LQ
printf("初始化后的队列的头指针为%p,尾指针为%p\n", LQ.front, LQ.rear);
InQueue(LQ, 4);
printf("尾指针为%p,入队元素为%d\n", LQ.rear, LQ.rear->data);
printf("出队元素为%d\n",OutQueue(LQ));
printf("头指针为%p,尾指针为%p\n", LQ.front, LQ.rear);
OutQueue(LQ); //此时无元素,测试无元素出队
GetElement(LQ); //此时无元素,测试无元素读队头元素
InQueue(LQ, 488);
InQueue(LQ, 378);
printf("队首元素为%d\n", GetElement(LQ));
return 0;
}