13.数据结构 循环队列_队列的顺序表示和实现

一.

在这里插入图片描述
在这里插入图片描述
队头元素总在[0]单元,不必设头指针,但出队元素时要移动大量元素,耗时长,效率低

#define QUEUE_INIT_SIZE 10//队列存储空间的初始分配量
#define QUEUE_INCREMENT 2//队列存储空间的分配增量
typedef int QElemType;
struct SqQueue1
{
	QElemType *base;//初始化的动态分配存储空间
	int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
	int queuesize;//当前分配的存储容量(以sizeof(QElemType)为单位)
};

在这里插入图片描述

void InitQueue(SqQueue1 &Q)
{
	//构造空队列
	Q.base = (QElemType*)malloc(QUEUE_INIT_SIZE*sizeof(QElemType));
	if (!Q.base)
	{
		exit(ERROR);//存储分配失败
	}
	Q.rear = 0;//空队列,尾指针为0
	Q.queuesize = QUEUE_INIT_SIZE;//初始存储容量
}

在这里插入图片描述

void DestroyQueue(SqQueue1 &Q)
{
	//销毁队列
	free(Q.base);//释放存储空间
	Q.base = NULL;
	Q.rear = Q.queuesize = 0;
}
void ClearQueue(SqQueue1 &Q)
{
	//清空队列
	Q.rear = 0;
}
Status QueueEmpty(SqQueue1 Q)
{
	//判断队列是否为空
	if (Q.rear == 0)
		return TRUE;
	else
		return FALSE;
}
int QueueLength(SqQueue1 Q)
{
	//返回Q的元素个数,即队列的长度
	return Q.rear;
}
Status GetHead(SqQueue1 Q, QElemType &e)
{
	//返回队头元素
	if (Q.rear)
	{
		e = *Q.base;
		return OK;
	}
	else
	{
		return ERROR;
	}
}

在这里插入图片描述

void EnQueue(SqQueue1 &Q, QElemType e)
{
	//插入新元素到队尾
	if (Q.rear == Q.queuesize)//当前存储空间已满
	{
		//增加分配
		Q.base = (QElemType*)realloc(Q.base, (Q.queuesize + QUEUE_INCREMENT)*sizeof(QElemType));
		if (!Q.base)
		{
			//分配失败
			exit(ERROR);
		}
		Q.queuesize += QUEUE_INCREMENT;//增加存储容量
	}
	Q.base[Q.rear++] = e;//入队新元素,队尾指针+1
}

在这里插入图片描述

Status DeQueue(SqQueue1 &Q, QElemType &e)
{
	//删除队头
	int i;
	if (Q.rear)//队列不空
	{
		e = *Q.base;
		for (i = 1; i < Q.rear; i++)//依次前移队列元素
		{
			Q.base[i - 1] = Q.base[i];
		}
		Q.rear--;
		return OK;
	}
	else
	{
		return ERROR;
	}
}
void QueueTraverse(SqQueue1 Q, void(*vi)(QElemType))
{
	//从队头到队尾依次对队列Q中每个元素调用函数vi()
	int i;
	for (i = 0; i < Q.rear; i++)
	{
		vi(Q.base[i]);
	}
	printf("\n");
}

void print1(QElemType i)
{
	printf("%d", i);
}

void main()
{
	Status j;
	int i, k = 5;
	QElemType d;
	SqQueue1 Q;
	InitQueue(Q);
	printf("初始化队列后,队列是否是空队列?%u(1:空 2:否)\n", QueueEmpty(Q));
	for (i = 1; i <= k; i++)
		EnQueue(Q, i);//依次入队k个元素
	printf("依次入队%d个元素后,队列中的元素为", k);
	QueueTraverse(Q, print1);
	printf("队列长度为%d,队列空否?%u(1:空 2:否)\n", QueueLength(Q), QueueEmpty(Q));
	DeQueue(Q, d);
	printf("出队一个元素,其值为%d\n", d);
	j = GetHead(Q, d);
	if (j)
		printf("现在队头元素是%d\n", d);
	ClearQueue(Q);
	printf("清空队列后,队列空否?%d(1:空 2:否)\n", QueueEmpty(Q));
	DestroyQueue(Q);
}

运行结果:
在这里插入图片描述

二.

另一种顺序存储结构,在出队元素时不移动元素,只改变头指针的位置
在这里插入图片描述

struct SqQueue2
{
	QElemType *base;//初始化的动态分配存储空间
	int front;//头指针,若队列不空,指向队列头元素
	int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
	int queuesize;//当前分配的存储容量(以sizeof(QElemType)为单位)
};

在这里插入图片描述
在这里插入图片描述

//顺序队列的基本操作
void InitQueue(SqQueue2 &Q)
{
	Q.base = (QElemType*)malloc(QUEUE_INIT_SIZE*sizeof(QElemType));
	if (!Q.base)
	{
		exit(ERROR);
	}
	Q.front = Q.rear = 0;
	Q.queuesize = QUEUE_INIT_SIZE;
}
void DestroyQueue(SqQueue2 &Q)
{
	if (Q.base)
		free(Q.base);
	Q.base = NULL;
	Q.front = Q.rear = Q.queuesize = 0;
}
void ClearQueue(SqQueue2 Q)
{
	Q.front = Q.rear = 0;
}
Status QueueEmpty(SqQueue2 Q)
{
	if (Q.front == Q.rear)
		return TRUE;
	else
		return FALSE;
}
Status GetHead(SqQueue2 Q, QElemType &e)
{
	if (Q.front == Q.rear)
		return ERROR;
	e = Q.base[Q.front];
	return OK;
}
//顺序非循环队列的基本操作
int QueueLength(SqQueue2 Q)
{
	return (Q.rear - Q.front);
}

在这里插入图片描述

void EnQueue(SqQueue2 &Q, QElemType e)
{
	if (Q.rear == Q.queuesize)
	{
		//队满,增加存储单元
		Q.base = (QElemType*)realloc(Q.base, (Q.queuesize + QUEUE_INCREMENT)*sizeof(QElemType));
		if (!Q.base)//增加单元失败
			exit(ERROR);
	}
	Q.base[Q.rear++] = e;
}

在这里插入图片描述

Status DeQueue(SqQueue2 &Q, QElemType &e)
{
	if (Q.front == Q.rear)
		return ERROR;
	e = Q.base[Q.front++];
	return OK;
}
void QueueTraverse(SqQueue2 Q, void(*vi)(QElemType))
{
	int i = Q.front;
	while (i != Q.rear)
	{
		vi(Q.base[i++]);
	}
	printf("\n");
}

void print2(QElemType i)
{
	printf("%d", i);
}
void main()
{
	Status j;
	int i, n = 11;
	QElemType d;
	SqQueue2 Q;
	InitQueue(Q);
	printf("初始化队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	printf("队列的长度为%d\n", QueueLength(Q));
	printf("请输入%d个整形队列元素:\n", n);
	for (i = 0; i < n; i++)
	{
		scanf("%d", &d);
		EnQueue(Q, d);
	}
	printf("队列长度为%d\n", QueueLength(Q));
	printf("现在队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	printf("现在队列中的元素为\n");
	QueueTraverse(Q, print2);
	DeQueue(Q, d);
	printf("删除队头元素%d\n", d);
	printf("队列中的元素为\n");
	QueueTraverse(Q, print2);
	j = GetHead(Q, d);
	if (j)
		printf("队头元素为%d\n", d);
	else
		printf("无队头元素(空队列)\n");
	ClearQueue(Q);
	printf("清空队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	j = GetHead(Q, d);
	if (j)
		printf("队头元素为%d\n", d);
	else
		printf("无队头元素(空队列)\n");
	DestroyQueue(Q);
}

运行结果:
在这里插入图片描述
二所示的队列顺序存储结构,在出队时,只是改变头指针的位置,不移动元素,可简化操作,节约时间。但这种队列顺序存储结构也有缺点,队列的每个存储空间自始至终只能存一个队列元素。即使这个队列元素出队后,其他的队列元素也不能占用这个存储空间。尤其在队列长度补偿,入队出队频繁的情况下,存储空间浪费较大。由于没有其他数据覆盖,当队头元素出队后,其值还保留在队列中。

三.

下面的方式克服了二的方式存储空间浪费较大的缺点,三采用循环队列:当队尾元素占据了存储空间的最后一个单元时,如再有新的元素入队,不说申请新的存储空间,而是将新元素插到存储空间的第一个单元,只要这个单元为空(元素已出队)。通过头尾指针对存储空间MAX_QSIZE求余做到这一点,形成循环队列。
在循环队列中,队尾指针可能小于队头指针。入队元素时,队尾指针加1.当队列满时,队尾指针等于队头指针,和队列空的条件一样。为了区别队满和队空,在循环队列中少用一个存储单元。也就是在存储空间为MAX_SIZE的循环队列中,最多只能存放MAX_SIZE-1个元素。这样,队列空的条件仍为队尾指针等于队头指针,队列满的条件改为(队尾指针+1)对MAX_SIZE求余等于队头指针。
在这里插入图片描述

#define MAX_QSIZE 5 //最大队列长度+1
struct SqQueue
{
	QElemType *base;//初始化的动态分配存储空间
	int front;//头指针,若队列不空,指向队列头元素
	int rear;//尾指针,若队列不空,指向队列尾元素的下一个位置
};

在这里插入图片描述

void InitQueue(SqQueue &Q)
{
	Q.base = (QElemType*)malloc(MAX_QSIZE*sizeof(QElemType));
	if (!Q.base)
		exit(OVERFLOW);
	Q.front = Q.rear = 0;
}

在这里插入图片描述

void DestroyQueue(SqQueue &Q)
{
	if (Q.base)
		free(Q.base);
	Q.base = NULL;
	Q.front = Q.rear = 0;
}
void ClearQueue(SqQueue &Q)
{
	Q.front = Q.rear = 0;
}
Status QueueEmpty(SqQueue Q)
{
	if (Q.front == Q.rear)
		return TRUE;
	else
		return FALSE;
}
int QueueLength(SqQueue Q)
{
	int i = (Q.rear - Q.front + MAX_QSIZE) % MAX_QSIZE;
	return i;
}
Status GetHead(SqQueue Q, QElemType &e)
{
	if (Q.front == Q.rear)
		return FALSE;
	e = Q.base[Q.front];
	return OK;
}

在这里插入图片描述

Status EnQueue(SqQueue &Q, QElemType e)
{
	if ((Q.rear + 1) % MAX_QSIZE == Q.front)//队列满
		return ERROR;
	Q.base[Q.rear] = e;
	Q.rear = (Q.rear + 1) % MAX_QSIZE;
	return OK;
}

在这里插入图片描述

Status DeQueue(SqQueue &Q, QElemType &e)
{
	if (Q.front == Q.rear)//队列空
		return ERROR;
	e = Q.base[Q.rear];
	Q.front = (Q.front + 1) % MAX_QSIZE;
	return OK;
}
void QueueTraverse(SqQueue Q, void(*vi)(QElemType))
{
	int i;
	i = Q.front;
	while (i != Q.rear)
	{
		vi(Q.base[i]);
		i = (i + 1) % MAX_QSIZE;
	}
	printf("\n");
}

void print3(QElemType i)
{
	printf("%d", i);
}
void main()
{
	Status j;
	int i = 0, l;
	QElemType d;
	SqQueue Q;
	InitQueue(Q);
	printf("初始化队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	printf("请输入整形队列元素(不超过%d个),-1为提前结束符:", MAX_QSIZE - 1);
	do
	{
		scanf("%d", &d);
		if (d == -1)
			break;
		i++;
		EnQueue(Q, d);
	} while (i < MAX_QSIZE - 1);
	printf("队列长度为%d\n", QueueLength(Q));
	printf("现在队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	printf("连续%d次由队头删除元素,队尾插入元素:\n", MAX_QSIZE);
	for (l = 1; l <= MAX_QSIZE; l++)
	{
		DeQueue(Q, d);
		printf("删除的元素是%d,请输入待插入的元素:", d);
		scanf("%d", d);
		EnQueue(Q, d);
	}
	l = QueueLength(Q);
	printf("现在队列中的元素为\n");
	QueueTraverse(Q, print3);
	printf("共向队尾插入了%d个元素:\n", l - 2);
	while (QueueLength(Q) > 2)
	{
		DeQueue(Q, d);
		printf("删除的元素值为%d\n", d);
	}
	j = GetHead(Q, d);
	if (j)
		printf("现在的队头元素为%d\n", d);
	ClearQueue(Q);
	printf("清空队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	DestroyQueue(Q);
}

运行结果:
在这里插入图片描述
三所采用的循环顺序队列存储结构,当队列的长度大于MAX_QSIZE-1时,无法动态的增加存储空间,原因是MAX_QSIZE是固定的常量。为了使循环队列也能动态的增加存储空间,不固定队列长度,把队列长度也作为结构体的一个成员。

四.

int QueueLength(SqQueue2 Q)
{
	int i;
	i = (Q.rear - Q.front + Q.queuesize) % Q.queuesize;
	return i;
}

在这里插入图片描述

void EnQueue(SqQueue2 &Q, QElemType e)
{
	int i;
	if ((Q.rear + 1) % Q.queuesize == Q.front)
	{
		//队列满,增加存储单元
		Q.base = (QElemType*)realloc(Q.base, (Q.queuesize + QUEUE_INCREMENT)*sizeof(QElemType));
		if (!Q.base)
			exit(ERROR);
		if (Q.front > Q.rear)//形成循环
		{
			for (i = Q.queuesize - 1; i >= Q.front; i--)
				Q.base[i + QUEUE_INCREMENT] = Q.base[i];//移动高端元素到新的高端
			Q.front += QUEUE_INCREMENT;//移动队头指针
		}
		Q.queuesize += QUEUE_INCREMENT;//增加队列长度
	}
	Q.base[Q.rear] = e;//将e插入队尾
	Q.rear = ++Q.rear%Q.queuesize;//移动队尾指针
}

在这里插入图片描述

Status DeQueue(SqQueue2 &Q, QElemType &e)
{
	if (Q.rear == Q.front)
		return ERROR;
	e = Q.base[Q.front];//用e返回队头元素
	Q.front = ++Q.front%Q.queuesize;//移动队头指针
	return OK;
}
void QueueTraverse(SqQueue2 Q, void(*vi)(QElemType))
{
	int i = Q.front;//i指向队头
	while (i != Q.rear)//没到队尾
	{
		vi(Q.base[i]);
		i = ++i%Q.queuesize;//向后移动i指针
	}
	printf("\n");
}
void main()
{
	Status j;
	int i, n = 11;
	QElemType d;
	SqQueue2 Q;
	InitQueue(Q);
	printf("初始化队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	printf("队列的长度为%d\n", QueueLength(Q));
	printf("请输入%d个整形队列元素:\n", n);
	for (i = 0; i < n; i++)
	{
		scanf("%d", &d);
		EnQueue(Q, d);
	}
	printf("队列长度为%d\n", QueueLength(Q));
	printf("现在队列空否?%u(1:空 2:否)\n"QueueEmpty(Q));
	printf("现在队列中的元素为\n");
	QueueTraverse(Q, print1);
	for (i = 0; i <= 3; i++)
	{
		DeQueue(Q, d);
	}
	printf("由队头删除3个元素,最后删除的元素为%d\n", d);
	printf("现在队列中的元素为\n");
	QueueTraverse(Q, print1);
	j = GetHead(Q, d);
	if (j)
		printf("队头元素为%d\n", d);
	else
		printf("无队头元素(空队列)\n");
	for (i = 1; i <= 5; i++)
		EnQueue(Q, i);
	printf("依次向队尾插入1-5,现在队列中的元素为\n");
	QueueTraverse(Q, print1);
	ClearQueue(Q);
	printf("清空队列后,队列空否?%u(1:空 2:否)\n", QueueEmpty(Q));
	j = GetHead(Q, d);
	if (j)
		printf("队头元素为%d\n", d);
	else
		printf("无队头元素(空队列)\n");
	DestroyQueue(Q);
}

运行结果:
在这里插入图片描述

五.利用非循环顺序队列采用广度搜索法求解迷宫问题(一条路径)

在这里插入图片描述

#define D 8//移动方向数,只能取4和8(8个可斜行;4个只可直走)
struct PosType//迷宫坐标位置类型
{
	int x;//行值
	int y;//列值
};
typedef struct
{
	PosType seat;//当前点的行值、列值
	int pre;//前一点在队列中的序号
}QElemType,SElemType;//栈元素和队列元素
struct
{
	int x, y;
}move[D] = {
#if D == 8
	{ 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 }, { -1,-1 }, { -1,0 }, { -1,1 }};
#endif
#if D == 4
	{ 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }};
#endif
void Path()
{
	//广度搜索法求一条迷宫路径
	SqQueue2 q;//采用非循环顺序队列
	QElemType qf, qt;//当前结点和下一点
	SqStack s;//采用顺序栈
	int i, flag = 1;//当找到出口,flag=0
	qf.seat.x = begin1.x;//将入口作为当前点
	qf.seat.y = begin1.y;
	qf.pre = -1;//设入口(第一点)的上一点的序号=-1
	m[qf.seat.x][qf.seat.y] = -1;//初始点设为-1(标记已访问过)
	InitQueue(q);
	EnQueue(q, qf);//起点入队
	while (!QueueEmpty(q) && flag)
	{
		//队列中还有没被广度搜索过的点且还没找到出口
		DeQueue(q, qf);//出队qf为当前点
		for (i = 0; i < D; i++)
		{
			//向各个方向尝试
			qt.seat.x = qf.seat.x + move[i].x;//下一个点的坐标
			qt.seat.y = qf.seat.y + move[i].y;
			if (m[qt.seat.x][qt.seat.y] == 1)
			{
				//此点是通道且不曾被访问过
				m[qt.seat.x][qt.seat.y] = -1;//标记已访问过
				qt.pre = q.front - 1;//qt的前一点处于队列中现队头减1的位置(没删除)
				EnQueue(q, qt);//入队qt
				if (qt.seat.x == end.x && qt.seat.y == end.y)//到达终点
				{
					flag = 0;
					break;
				}
			}
		}
	}
	if (flag)//搜索完整个队列还没到达终点
		printf("没有路径可到达终点!\n");
	else
	{
		//到达终点
		InitStack(s);//初始化s栈
		i = q.rear - 1;//i为待入栈元素在队列中的位置
		while (i >= 0)//没到入口
		{
			Push(s, q.base[i]);//将队列中的路径入栈(栈底为出口,栈顶为入口)
			i = q.base[i].pre;//i为前一元素在队列中的位置
		}
		i = 0;//i为走出迷宫的足迹
		while (!StackEmpty(s))
		{
			Pop(s, qf);//依照由入口到出口的顺序弹出路径
			i++;
			m[qf.seat.x][qf.seat.y] = i;//标记路径为足迹(标记前的值为-1)
		}
		printf("走出迷宫的一个方案:\n");
		Print();//输出m数组
	}
}
void main()
{
	Init(1);//初始化迷宫,通道值为1
	Path();//求一条迷宫路径
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值