数据结构(循环队列)

今天我们谈一谈循环队列,首先什么是循环队列,为什么要有循环队列了?
首先我们需要知道对于队列的实现可以使用链表,也可以使用数组,这个我们在前面的博客中谈过了,使用链表就遵循队列的先进先出原则,尾部插入(入队),头部删除(出队),不用考虑扩容(堆的空间足够),并且在出队时,只要移动头部指针,释放头一个元素结点就可以了,但是对于数组去实现队列,就会造成一个问题,我们举例:

void QueueOut(QueueNode *p){
	assert(p != NULL);
	if (QueueEmpty(p)){              //判空函数
		printf("队列为空,无法出队!\n");
	}
	p->front++;             //出队只需要不去读取它就可以了。
}

在这里插入图片描述
上面我模拟了一下简单数组实现队列的出队过程,但是这是我们如果想要入队是不可以的,因为队尾数字已经超过数组的下标,这是只能扩容才可以实现对元素的入队,但是这样就会出现"假满"现象,造成空间浪费,因此我们我们出现了循环队列,简单介绍一下这个循环队列
在这里插入图片描述
上边这个长的像"车轮"的就是循环队列,这只是我们心里想的一个模样,实际在内存中,可没有这样的,所以想要达到这样的效果,我们就需要用到我们的去模运算,在实现循环队列之前,还有一个问题,就是如何去判断这个队列是空还是满,在前面我只是用链表的方式实现了栈和队列,但是在数组方面,大家应该知道,数组实现队列,判空的条件就是队头和队尾相同,但是如何判满了,简单的数组队列,判满很简单,就是队尾的数字已经大于等于容量,则就说明满了,但是对于循环队列,我们这里我取模运算,一去模就会归零,这样就会造成空和满的条件相同,那这就好了,所以我们就想了一个办法,在申请空间时多申请一个空间,而不去使用它,这个空间只是将空和满的判断条件区分开,好了,基本的问题就这样,我们就进入今天的代码之路:
一、结构的定义与初始化

//结构的定义
#define Capacity 7
#define QUData int
typedef struct QueueNode{
	QUData *base;
	int capacity;
	QUData front;
	QUData rear;
}QueueNode;
//初始化
void QueueInit(QueueNode *p){
	assert(p != NULL);
	p->capacity = Capacity;
	p->base = (QUData *)malloc(sizeof(QUData)*(p->capacity+1));//多申请一个空间
	p->front = p->rear = 0;
}

二、判满与判空

//判空
bool QueueEmpty(QueueNode *p){
	assert(p != NULL);
	return p->front == p->rear;
}
//判满
bool QueueFull(QueueNode *p){
	assert(p != NULL);
	return (p->rear + 1) % (p->capacity+1) == p->front;
}

三.入队

//入队
void QueueEn(QueueNode *p,QUData x){
	assert(p != NULL);
	if (QueueFull(p)){
		printf("队列已满,无法入队!\n");
		return;
	}
	p->base[p->rear] = x;
	p->rear++;
	p->rear = (p->rear + 1) % (p->capacity + 1);
}

四、出队

//出队
void QueueOut(QueueNode *p){
	assert(p != NULL);
	if (QueueEmpty(p)){
		printf("队列为空,无法出队!\n");
	}
	p->front= (p->front + 1) % (p->capacity + 1);
}

五、查看队头、队尾元素、元素个数

//查看队头元素
QUData QueueFrontData(QueueNode *p){
	assert(p != NULL);
	assert(!QueueEmpty(p));
	return p->base[p->front];
}
//查看队尾元素
QUData QueueBackData(QueueNode *p){
	assert(p != NULL);
	assert(!QueueEmpty(p));
	return p->base[(p->rear - 1 + p->capacity + 1) % (p->capacity + 1)];
}

//元素个数
size_t QueueSize(QueueNode *p){
	assert(p != NULL);
	if (p->front > p->rear){
		return p->front + p->capacity + 1 - p->rear;
	}
	else{
		return p->rear - p->front;
	}
}

六、遍历元素

//遍历
void QueueShow(QueueNode *p){
	assert(p != NULL);
	for (int i = p->front; i != p->rear;){
		printf("%d ", p->base[i]);
		i = (i+1) % (p->capacity + 1);
	}
}

七、清除数据与摧毁队列

/清除
void QueueClear(QueueNode *p){
	assert(p != NULL);
	p->front = p->rear = 0;
}
//摧毁
void QueueDestroy(QueueNode *p){
	assert(p != NULL);
	free(p->base);
	p->base = NULL;
	p->capacity = p->front = p->rear = 0;
}

上面就是关于循环队列的相关简单操作,很容易看出,与数组实现的简单队列相比,多了很多的模运算,在这里我就不在谈了,大家只要去画出循环队列的图像,然后根据图像进行编写,就会非常简单!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值