数据结构之顺序循环队列的基本操作(非常详细C语言版)

----------------------✨==!!!热烈欢迎各位大佬!!!==✨---------------------

作者: BooleanChar12

博客主页: BooleanChar12的博客

很喜欢的一句话Because we all stand on the shoulders of giants.

如有bug疑惑欢迎大家与我

觉得博主文章写的不错的话,希望大家三连(关注,点赞,评论),多多支持一下!!

--------------------------------------------------------------------------==专栏回顾==-------------------------------------------------------------------------------

☕Java(从入门到放弃) 数据结构(从放弃到入门) Android开发(从入门到入土)
计算机组成原理(从1到0) 计算机网络(从上网到退网) 操作系统(从0到0.1)
C语言练习题 Leetcode(狂刷笔记) 微信小程序开发日记

写在前面:因初学数据结构,读者如发现有错误或者疑惑,可评论私信,一起交流,如觉得写得不错,还请点赞,谢谢!

1.排忧解惑:

1.为什么front和rear是int类型的变量却还要叫指针?

个人看法:是因为顺序循环队列指向的是一块连续的内存空间(可以看作一个数组空间),
在操作队列的时候只存在地址加一位或者减一位的情况,
使用指针来表示在访问的时候反而更加麻烦,不如用数组的下标(就是这里定义的int front表示的其实是base[]数组的下标)来表示
当然也可以用指针来表示,下面相应的代码也要改变,只是对于操作这种连续且每次只会+1,-1的地址来说
用这种方式访问更加方便

2.如何判断循环队列是否满了?

​ 由于顺序循环队列的队空和队满的条件都是Q.frontQ.rear,就无法判断到底是队空还是队满,我们就得重新想一个判断条件
​ 解决方法:1.设计一个变量记录队中元素个数,个数为0则队空,个数为MAXQSIZE则队满
​ 2.设计一个标志以区别队空和队满
​ 3.少用一个队列格子,即用(Q.rear+1)%MAXQSIZEQ.front来判断,即下一个rear所在的位置与对头front所在位置相等
​ 注:下面代码采用的是第三种方式判断队空或者队满

3.浅谈为什么要用到%运算?

个人看法:1.%取模运算的本质是让运算后的结果落入0~这个模数中,
其实就是循环减去一个数直到被模的数小于模数(即被模的数不够减去1*模数的时候循环终止),然后将被模的数返回作为模运算的结果
为什么这么说呢,举例:20%6=2

				1.循环开始:第一次		20-6=14
				2.			第二次		14-6=8
				3.			第三次		8-6=2
				4.循环终止:第四次		2-6发现减不过,终止循环,把2返回作为本次模运算的结果 
			
			再比如:1%6=1 2%6=2 3%6=3 4%6=4 5%6=5 6%6=0 7%6=1 8%6=2 9%6=3......
			不难看出,无论哪个正整数%6,最后的结果一定都在[0,6]这个区间内,
			而且随着正整数的增大,结果是每次+1,并且循环的,即0,1,2,3,4,5,6,0,1,2,3,4,5,6,0......
			所以我们巧妙利用%运算的这个特点,把实际的线性顺序内存可以当作循环顺序内存使用 

4.解读Q.rear = (Q.rear+1)%MAXQSIZE

​ 1.如果 Q.rear+1 ​ 2.如果 Q.rear+1>MAXQSIZE之后,即发生假溢出,则运算结果就会从进入新的[0,100]的循环,%运算保证每次rear++后的结果落在[0,100]之间
​ 3.下面代码中的 Q.front = (Q.front+1)%MAXQSIZE同理

5.为什么队列的长度不是Q.rear-Q.front而是(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE?

1.普通队列的长度:length = Q.rear-Q.front
2.循环队列的长度: (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE
3.个人认为,(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE其实就是这个if语句的整合:
	 	if((Q.rear-Q.front)>0) {     
		printf("当前队列的长度:%d\n",Q.rear-Q.front);
	} else {
		printf("当前队列的长度:%d\n",Q.rear+MAXQSIZE-Q.front);
	}
	3.1如果Q.rear>Q.front,那队列长度就是Q.rear-Q.front
	3.2如果Q.rearQ.front,
		因为队列不满,Q.rear-Q.front的值一定小于1*MAXQSIZE,
		所以(Q.rear+MAXQSIZE-Q.front)的值一定大于1*MAXQSIZE,小于2*MAXQSIZE,
		所以(Q.rear+MAXQSIZE-Q.front)%MAXQSIZE的最终结果还是 Q.rear-Q.front
			eg:假设MAXQSIZE=6,某一状态时Q.rear=4,Q.front=2;(即队列入队4个元素后,出队2个元素,此时队长应为2)
				0<4-2<1*6
				0<4-2+6<2*6
				取模之后相当于把加的6给减去了,剩下的就是计算结果,即 4-2就是计算结果,也就是4-2就是队长  
	4.2如果Q.rear

2.代码实现:

# include 
# include //使用malloc()函数必须引入的库


#define MAXQSIZE 5 //定义Q队列的最大长度,相当于一个数组arr[100]就是arr[MAXSIZE] 
#define OK 1  		 //定义返回状态码 return OK就是return 1    		在函数正确执行完的时候用 
#define ERROR 0		 //定义返回状态码 return ERROR就是return 0 		
#define OVERFLOW -2	 //定义返回状态码 return OVERFLOW就是return -2  在内存溢出的时候用 

typedef int QElemType;//定义下面代码中出现的QElemType就是表示int
typedef int Status; //定义下面代码中出现的Status就是表示int
//队列结构体的定义
typedef struct {
	QElemType * base;//指向动态分配内存的首地址
	int front;//头指针
	int rear;//尾指针

} SqQueue;

//1.队列初始化
Status InitQueue(SqQueue &Q) {
	Q.base = (QElemType *)malloc(MAXQSIZE*sizeof(QElemType));//动态创建连续的内存空间并将此内存空间的首地址赋给Q队列的base指针,即Q队列的base指针指向了这块连续的内存空间
	if(!Q.base) { //判断能不能在内存中找到这样一块连续空间,没有找到就exit(OVERFLOW),即返回状态OVERFLOW
		printf("内存溢出,无法开辟空间!");
		return OVERFLOW;
	}
	//如果找到了,就让Q队列的头指针和尾指针都等于0,即指向base[]数组的0的位置,即那块内存空间的首地址
	Q.front=Q.rear=0;
	printf("初始化队列成功!\n");
	//返回状态OK
	return OK;
}

//2.入队
Status EnQueue(SqQueue &Q) {
	//入队的时候首先需要判断队列是否满了

	if((Q.rear+1)%MAXQSIZE==Q.front) {
		printf("当前队满!\n");
		return OVERFLOW;
	}

	int input;//定义input变量接收用户输入的入队元素,用int声明的变量的空间是由系统自动分配的,当然这里也可以使用malloc动态创建内存空间
	printf("请输入入队元素:\n");
	scanf("%d",&input);
	Q.base[Q.rear] = input;//从队尾入队,把输入的数据放到Q队列的base数组的下标为Q.rear的位置
	Q.rear = (Q.rear+1)%MAXQSIZE;//rear++,这里的++在循环队列中要用(Q.rear+1)%MAXQSIZE,来解决假溢出的问题

	return OK;
}

//3.出队
Status DeQueue(SqQueue &Q) {
	//出队的时候首先判断队列是否为空		当然这里可以直接使用已经写好的判断队空的QueueEmpty()函数
	if(Q.front==Q.rear) {
		printf("当前队空,请先入队!\n");
		return ERROR;
	}
	int output;
	output = Q.base[Q.front];//从队头入队,记录出队数据
	//front++,同样这里的++在循环队列中要用(Q.front+1)%MAXQSIZE,来解决假溢出的问题
	Q.front = (Q.front+1)%MAXQSIZE;
	//打印出队元素
	printf("出队元素为:%d\n",output);
	return OK;
}

//4.取队头元素
Status GetHead(SqQueue &Q) {
	//队列不空的时候才能取队头元素		当然这里可以直接使用已经写好的判断队空的QueueEmpty()函数
	if(Q.front==Q.rear) {
		printf("当前队空,请先入队!\n");
		return ERROR;
	}
	int head;
	head = Q.base[Q.front];//将当前队头元素的值赋给head变量
	printf("当前队头元素为:%d\n",head);
	return OK;
}

//5.判断队列是否为空
Status QueueEmpty(SqQueue &Q) {
	//直接把上面出队时的判空逻辑复制过来,因为判断条件都是一样的
	if(Q.front==Q.rear) {
		printf("当前队空,请先入队!\n");
		return ERROR;
	}
	printf("当前队列不为空\n");//否则当前队列不为空
}

//6.遍历队列中的元素TravelQueue
Status TravelQueue(SqQueue &Q) {
	//先判断队列是否为空   		当然这里可以直接使用已经写好的判断队空的QueueEmpty()函数
	if(Q.front==Q.rear) {
		printf("当前队空,请先入队!\n");
		return ERROR;
	}
	//为防止遍历一次就把Q中的rear或者front的值修改,所以这里新建一个变量来接受rear或front的值
	//当然不想重新定义一个变量来保存front的值的话,可以在该函数的参数部分改为(SqQueue Q),
	//但是这样相当于在内存中重新拷贝了一份Q,而且每次遍历都会这样拷贝一份,内存开销大,速度和效率都会降低
	int front2 = Q.front;
	printf("当前队列中的元素为:");
	while(!(front2==Q.rear)) {
		printf("%d  ",Q.base[front2]);//输出当前base[front2]所表示的元素
		front2=(front2+1)%MAXQSIZE;//让front2表示下一次所在的位置
	}
	printf("\n");
	return OK;
}

//7.求当前队列长度
Status QueueLength(SqQueue &Q) {
	//printf("当前队列的长度:%d\n",(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
	return OK;
}

//8.销毁队列DestroyQueue
Status DestoryQueue(SqQueue &Q) {
	//让front和rear相等即可
	Q.front=Q.rear;
	printf("队列销毁成功!"); 
	return OK;
}

int main(void) {
	SqQueue Q;//创建一个队列结构体Q
	int option;//变量option用于判断用户选择的栈操作
	int loop=1;//变量loop表示循环的条件
	do {
		//菜单
		printf("1.初始化队列\n");
		printf("2.判断当前队列是否为空\n");
		printf("3.入队\n");
		printf("4.出队\n");
		printf("5.取队头元素\n");
		printf("6.获取当前队列长度\n");
		printf("7.遍历队列中元素\n");
		printf("8.销毁队列\n");
		printf("9.退出系统\n");
		printf("请输入你的操作:");
		scanf("%d",&option);
		switch(option) {
			case 1://初始化队列
				InitQueue(Q);//调用初始化队列的函数,传入Q这个队列
				break;
			case 2://判断当前队列是否为空
				QueueEmpty(Q);
				break;
			case 3://入队
				EnQueue(Q);
				break;
			case 4://出队
				DeQueue(Q);
				break;
			case 5://取队头元素
				GetHead(Q);
				break;
			case 6://获取当前队列长度
				QueueLength(Q);
				break;
			case 7://遍历队列中元素
				TravelQueue(Q);
				break;
			case 8://销毁队列
				DestoryQueue(Q);
				break;
			case 9://退出系统
				loop = 0;
				printf("你退出了系统......");
		}
		printf("\n\n"); //每次执行完换两行,对用户视觉友好
	} while(loop);//循环条件为loop
}

你可能感兴趣的:(数据结构(从放弃到入门),c语言,数据结构,经验分享)