数据结构 第四章 队列(一)

在这里插入图片描述

🚀 写在最前:这篇文章将学习队列这种结构,以及该结构的一些基本操作的实现,包括顺序存储队列和链式存储队列的基本操作的实现
🚀:点个关注吧😀,让我们一起探索计算机的奥秘!

一、队列的定义

队列和栈差不不多,它也是一种受限制的线性表,它受的限制是它只能它线性表的一端插入数据(即入队),只能在线性表的另外一端删除数据(即出队)。

二、队列的基本操作

  • 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;
}

在这里插入图片描述

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值