zzu数据结构实验(四) 队列的基本操作和实现(应用:中心对称检验)

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.4k人参与

前言 代码仓库链接

本项目已经开源在github仓库:zzu_DataStructure_Experiment
🐑 zzu_DataStructure_Experiment:是一个用于学习和练习核心数据结构的代码仓库。它包含zzu的所有数据结构实验,涉及数组、链表、栈、队列、树、图等多种数据结构的实现、示例和测试用例,帮助读者强化编程能力并提升算法思维。
♥️ 如果你觉得项目对你有用,请留下一个 ⭐️ Star来支持作者,不胜感激~ ♥️

1 实验目的

  熟练掌握栈和队列的抽象数据类型,能在相应的应用问题中正确选用它们,熟练掌握栈和队列的实现方法(顺序和链式),两种存储结构和基本操作的实现算法,注意空和满的判断条件及它们的描述方法,掌握循环队列与其它顺序结构实现上的不同及解决办法,熟悉各种队列的基本操作在循环队列上的实现。

2 实验内容

  • 用队列实现形如a + b@b + a# 的中心对称的字符序列的检验。
  • 选择合适的存储结构(循环队列)表示队列,解决队空、队满判断条件相同的矛盾,实现基于循环队列的存储结构的基本操作,初始化、队空/满判断,入队、出队、取队头元素、求队列长度等,对所写出的算法进行时间复杂度分析。

3 实验操作

3.1 函数声明和队列数据结构定义

  和之前的实验不同,这里的实验在单个文件(exp04.cpp)完成。

  代码首先定义了循环队列(Circular Queue)的基本操作及其相关功能。循环队列是一种特殊的队列结构,利用数组存储数据元素,通过头指针(front)和尾指针(rear)的移动实现“首尾相接”的循环存储方式。
  代码中定义了循环队列的基本结构体 sqqueue,并声明了一系列操作函数,包括队列的初始化、入队、出队、判断空/满、获取队头元素、销毁队列等。此外,还声明了一个名为 CenterMatch 的函数,用于判断一个字符串是否为中心对称(回文)序列。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define maxqsize 20
typedef char qelemtype;
typedef int status;
//约定以少用一个元素空间,以“队列头指针在队列尾指针的下一位置”作为队列满的标志
typedef struct {
	qelemtype* base;
	int front;
	int rear;
}sqqueue;
//1.初始化队列
status InitQueue(sqqueue& Q);
//2.求队列长度
int QueueLength(sqqueue Q);
//3.入队列
status EnQueue(sqqueue& Q, qelemtype e);
//4.队前元素出队列
status DeQueueFront(sqqueue& Q, qelemtype& e);
//5.队后元素出队列
status DeQueueRear(sqqueue& Q, qelemtype& e);
//6.判断是否队空
status QueueEmpty(sqqueue Q);
//7.判断是否队满
status QueueFull(sqqueue Q);
//8.获取队头元素
status GetHead(sqqueue Q, qelemtype& e);
//9.摧毁队列
status DestroyQueue(sqqueue& Q);
//中心对称的字符序列的检验
int CenterMatch(char* str);

  2️⃣ 接着函数声明部分定义了循环队列的基本操作和扩展功能(中心对称检查):

序号函数声明功能说明
1status InitQueue(sqqueue& Q);初始化队列,分配存储空间并设置头尾指针
2int QueueLength(sqqueue Q);返回队列中元素的个数
3status EnQueue(sqqueue& Q, qelemtype e);将元素 e 插入队列尾部(入队)
4status DeQueueFront(sqqueue& Q, qelemtype& e);删除队头元素并用 e 返回(前端出队)
5status DeQueueRear(sqqueue& Q, qelemtype& e);删除队尾元素并用 e 返回(后端出队)
6status QueueEmpty(sqqueue Q);判断队列是否为空
7status QueueFull(sqqueue Q);判断队列是否已满
8status GetHead(sqqueue Q, qelemtype& e);获取队头元素(不出队)
9status DestroyQueue(sqqueue& Q);销毁队列,释放空间
10int CenterMatch(char* str);判断字符串是否为中心对称(回文)序列

3.2 队列的基本操作实现

🧩 3.2.1 基本操作1 初始化队列

  该函数用于创建并初始化循环队列。通过 malloc 动态分配一段大小为 maxqsize 的存储空间给 Q.base,并将 frontrear 都置为 0,表示队列为空。执行成功返回 OK,若内存分配失败则退出程序。具体代码如下:

//1.初始化栈
status InitStack(sqstack& S) {
	S.base = (elemtype*)malloc(stackinitsize * sizeof(elemtype));
	if (!S.base)exit(OVERFLOW);
	S.top = S.base;
	S.stacksize = stackinitsize;
	return OK;
}

🧩 3.2.2 基本操作2 求队列长度

  该函数通过 (Q.rear - Q.front + maxqsize) % maxqsize 计算当前队列中元素的个数。利用取模操作,能在循环数组中正确计算长度,无论 rear 是否“绕回”数组开头。具体代码如下:

//2.求队列长度
int QueueLength(sqqueue Q) {
	return (Q.rear - Q.front + maxqsize) % maxqsize;
}

🧩 3.2.3 基本操作3 入队列

  该函数用于将元素 e 插入队列尾部。首先判断 (Q.rear + 1) % maxqsize == Q.front 是否成立,若为真则表示队列已满,入队失败。否则,将元素存入 Q.base[Q.rear],然后将 rear 向后移动一个位置(循环意义上)。具体代码如下:

//3.入队列
status EnQueue(sqqueue& Q, qelemtype e) {
	if ((Q.rear + 1) % maxqsize == Q.front)return ERROR;
	Q.base[Q.rear] = e;
	Q.rear = (Q.rear + 1) % maxqsize;
	return OK;
}

🧩 3.2.4 基本操作4 队前元素出队列

  该函数用于删除队头元素并将其保存到变量 e 中。若 Q.front == Q.rear 表示队列为空,出队失败。否则,将队头元素取出,然后使 front 前移一位(循环方式),实现出队。具体代码如下:

//4.队前元素出队列
status DeQueueFront(sqqueue& Q, qelemtype& e) {
	if (Q.rear == Q.front)return ERROR;
	e = Q.base[Q.front];
	Q.front = (Q.front + 1) % maxqsize;
	return OK;
}

🧩 3.2.5 基本操作5 队后元素出队列

  该函数实现从队尾删除一个元素并返回它。如果队列为空则返回 ERROR。否则取出尾部元素 Q.base[Q.rear - 1],再将 rear 向前移动一位。具体代码如下:

//5.队后元素出队列
status DeQueueRear(sqqueue& Q, qelemtype& e) {
	if (Q.rear == Q.front)return ERROR;
	e = Q.base[Q.rear - 1];
	Q.rear = (Q.rear - 1) % maxqsize;
	return OK;
}

🧩 3.2.6 基本操作6 判断是否队空

  该函数通过 Q.front == Q.rear判断队列是否为空。若队列为空,返回 OK(1);否则返回 ERROR(0)。具体代码如下:

//6.判断是否队空
status QueueEmpty(sqqueue Q) {
	if (Q.front == Q.rear)return OK;
	else
		return ERROR;
}

🧩 3.2.7 基本操作7 判断是否队满

  当 (Q.rear + 1) % maxqsize == Q.front 时,表示队列已满。此时尾指针“追上”头指针的前一位置,返回 OK;否则返回 ERROR。具体代码如下:

//7.判断是否队满
status QueueFull(sqqueue Q) {
	if ((Q.rear + 1) % maxqsize == Q.front)return OK;
	else
		return ERROR;
}

🧩 3.2.8 基本操作8 获取队头元素

  该操作用于读取但不删除队头元素。若队列为空则返回 ERROR,否则将 Q.base[Q.front] 赋值给 e 并返回 OK。具体代码如下:

//8.获取队头元素
status GetHead(sqqueue Q, qelemtype& e) {
	if (Q.rear == Q.front)return ERROR;
	e = Q.base[Q.front];
	return OK;
}

🧩 3.2.9 基本操作9 摧毁队列

  该函数用于释放队列占用的内存空间。若 Q.base 存在,则先释放它,并将 front、rear 置空(即 NULL),防止野指针。销毁后返回 OK。具体代码如下:

//9.摧毁队列
status DestroyQueue(sqqueue& Q) {
	if (Q.base) {
		Q.front = Q.rear = NULL;
		free(Q.base);
	}
	return OK;
}

👍 3.3 实验内容实现:中心对称的字符序列的检验

   CenterMatch(char* str) 函数用于判断一个特定格式的字符串是否为中心对称(回文)序列。程序先初始化循环队列,将字符串中 ‘@’ 前和 ‘@’ 后、‘#’ 前的字符依次入队,然后从队头和队尾分别取出字符进行比较;若任意一对字符不相等则说明不对称,立即结束;若所有字符均成对相等,最终队列为空,则说明该字符串中心对称,函数返回 OK,否则返回 ERROR。具体代码如下:

//栈的括号匹配检验
status CheckMatch_Brackets(char* str) {
	sqstack S;
	InitStack(S);
	ClearStack(S);
	int len = strlen(str);
	elemtype e;
	int i;
	for (int i = 0; i < len; i++) {
		switch (str[i]) {
		case '{':
		case '[':
		case '(':
			Push(S, str[i]);
			break;
		case '}':
			if (S.top == S.base)
				return ERROR;
			Pop(S, e);
			if (e != '{')
				return ERROR;
			break;
		case ']':
			if (S.top == S.base)
				return ERROR;
			Pop(S, e);
			if (e != '[')
				return ERROR;
			break;
		case ')':
			if (S.top == S.base)
				return ERROR;
			Pop(S, e);
			if (e != '(')
				return ERROR;
			break;
		default:
			break;
		}
	}
	if (S.top != S.base)return ERROR;
	return OK;
}

3.6 主函数测试实现

  主函数用于对前面实现的队列基本操作和应用函数进行综合测试。程序首先初始化一个循环队列 Q,依次入队元素 ‘a’、‘b’、‘c’、‘d’,并在每次操作后输出当前队列长度,然后判断队列是否为空。接着进行多次出队操作,包括从队头和队尾删除元素,并显示出队的字符及当前队列长度,再次检查队列是否为空,最后销毁队列以释放内存。随后,程序进入中心对称检测部分,提示用户输入一个以 ‘@’ 为分隔符、‘#’ 为结尾标志的字符串,通过调用 CenterMatch() 函数判断其是否为中心对称序列,并输出相应结果。具体实现代码如下:

int main() {
	sqqueue Q;
	qelemtype e;
	InitQueue(Q);
	cout << "-----------循环队列的基本操作测试:----------" << endl;
	EnQueue(Q, 'a');
	cout << "入队列'a',队列长度为:" << QueueLength(Q) << endl;
	EnQueue(Q, 'b');
	cout << "入队列'b',队列长度为:" << QueueLength(Q) << endl;
	EnQueue(Q, 'c');
	cout << "入队列'c',队列长度为:" << QueueLength(Q) << endl;
	EnQueue(Q, 'd');
	cout << "入队列'd',队列长度为:" << QueueLength(Q) << endl;
	if (QueueEmpty(Q))
		cout << "队列为空!" << endl;
	else
		cout << "队列不为空!" << endl;
	DeQueueFront(Q, e);
	cout << "出队头元素:" << e << ",队列长度为:" << QueueLength(Q) << endl;
	DeQueueRear(Q, e);
	cout << "出队头元素:" << e << ",队列长度为:" << QueueLength(Q) << endl;
	DeQueueFront(Q, e);
	cout << "出队头元素:" << e << ",队列长度为:" << QueueLength(Q) << endl;
	DeQueueRear(Q, e);
	cout << "出队头元素:" << e << ",队列长度为:" << QueueLength(Q) << endl;
	if (QueueEmpty(Q))
		cout << "队列为空!" << endl;
	else
		cout << "队列不为空!" << endl;
	DestroyQueue(Q);
	cout << "----------是否是中心对称序列测试----------" << endl;
	char str[maxqsize + 1];
	cout << "请输入待检测的字符串(@做间隔,#做结尾):";
	cin >> str;
	if (CenterMatch(str))
		cout << "待检测序列是中心对称序列!" << endl;
	else
		cout << "待检测序列不是中心对称序列!" << endl;
	cout << "请输入待检测的字符串(@做间隔,#做结尾):";
	cin >> str;
	if (CenterMatch(str))
		cout << "待检测序列是中心对称序列!" << endl;
	else
		cout << "待检测序列不是中心对称序列!" << endl;
	return 0;
}

3.7 中心对称序列检验的队列应用示意图

  下面示意图展示了队列在中心对称序列检验中的工作过程。
  在中心对称检验中,以字符串 a+b@b+a# 为例,程序先将 结束标志 ‘#’ 之前的字符依次压入队列中,然后通过头指针和尾指针指向的队头和队尾元素出队进行比较,。若每个字符都相等,且最后队列为空,则说明该字符串是一个中心对称序列。
在这里插入图片描述

4 实验结果

4.1 项目目录结构

  项目在zzu_DataStructure_Experiment目录下的exp04文件夹。对应的文件结构为:

exp04/
├── sqQueue.cpp        --> 队列基本操作和应用实现和测试文件
└── readme_exp04.md    --> exp04项目运行操作和结果记录文件

4.2 实验运行配置和结果截图

  这里是单个cpp文件,因此没有编写额外的cmake配置,只需要单独运行单个文件:

以下命令适用于已经配置好c/c++编译的Mac系统。其他平台用户建议直接使用IDE(如Devc++、Vscode等)运行按钮运行。

  1️⃣ 打开终端,切换到项目目录,使用mac自带c++编译工具clang++编译:

cd Path/To/exp04
clang++ -std=c++17 sqQueue.cpp -o sqQueue

  2️⃣ 运行编译好的可执行程序sqQueue

./sqQueue

  若程序正确运行,得到以下实验结果:
在这里插入图片描述

5 报告总结

  算法复杂度分析及优、缺点分析

(1) 循环队列的基本操作中,由于队头指针和队尾指针的加入,循环队列的初始化、入队、出队、获取队列长度、判空、判满等基本操作的时间复杂度均为O(1)。利用静态数组增设队头、队尾指针模拟的循环队列,其大小固定不能更改,但是逻辑上简单易懂。

(2) 在用队列实现堆字符串序列的中心对称检验过程中,就是对队列中的元素遍历并进行比较的过程,因此该算法的时间复杂度为O(n)。该算法在使用过程中,用到了出队尾元素这一操作,因此该算法对队列的数据结构有一定的限制。

写在最后

  1️⃣ 创作不易,如果你觉得这篇文章对你有用,请点赞收藏关注~。同时如果你认为文章配套开源项目zzu_DataStructure_Experiment对你有用,请留下一个 ⭐️ Star来支持作者,不胜感激~
  2️⃣ 转载请注明出处~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值