【队列练习】 - 设计循环队列

题目描述

描述
设计你的循环队列实现。循环队列是一种线性数据结构,
其操作表现基于FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
你的实现应该支持如下操作:
1、MyCircularQueue(k) :构造器, 设置队列长度为k。
方法:开辟k+1个数组空间,初始化循环队列。
2、Front:从队首获取元素。如果队列为空,返回-1。
方法:循环队列不为空,返回queue[front]。
3、Rear:获取队尾元素。如果队列为空,返回-1。
方法:循环队列不为空,如果tail等于0,返回queue[k],否则返回queue[tail-1]。
4、enQueue(value):向循环队列插入一个元素。如果成功插入则返回真。
方法:循环队列是否满,queue[tail]=x;tail++;修正循环队列的下标tail %= (k + 1);
5、deQueue() :从循环队列中删除一个元素。 如果成功删除则返回真。
方法:循环队列是否为空,front++;修正循环队列的下标front %= (k + 1);
6、isEmpty() :检查循环队列是否为空。
方法:front 等于 tail,返回真。
7、isFull() :检查循环队列是否已满。
方法:(tail+1) % (k+1) 等于 front,返回真。


解题分析:

循环队列的特性:1、符合先进先出 2、空间大小是固定的

思路:
循环队列,无论使用数组实现还是链表实现,都要多开一个空间。
1、如果是数组也就意味着,要是一个存k个数据的循环队列, 要开k + 1个空间否则无法实现判空和判满。
2、如果是链表也就意味着,要是一个存k个数据的循环队列, 要开k + 1个结点否则无法实现判空和判满。(链表结点必须开辟好)
因为初始时front == tail可以确定为空,但下一次当front == tail时到底是空还是满,有歧义。
循环队列,可以用数组实现,通过下标控制。也可以用链表实现,即循环链表。
1、当用数组实现时,下标front == tail表示空,(tail+1)%(K+1) == front表示满
2、当用链表实现时,指针front == tail表示空,tail->next == front表示满


代码实现:

1、MyCircularQueue* myCircularQueueCreate(int k);创建循环队列

typedef struct {
	int* a;//利用指针维护的数组队列
	int front;//队头下标
	int tail;//队尾下标
	int k;//队列容量
}MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k)
{
	MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	if (cq == NULL)
		return NULL;
	cq->a = (int*)malloc(sizeof(int) * (k + 1));//多开一个空间,区分空和满
	cq->front = cq->tail = 0;
	cq->k = k;

	return cq;
	//为什么不定义MyCircularQueue cq;再返回&cq。因为不能返回局部变量的地址。你也可以使用传址调用通过形参返回。
}

2、bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);入队列

//入队列成功,返回真,没有成功(队列满了),返回假
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
	if (myCircularQueueIsFull(obj))
	{
		return false;
	}

	obj->a[obj->tail] = value;//队尾入数据
	++obj->tail;
	obj->tail %= (obj->k + 1);//当插入的次数超过4次,也就是下标大于k,下标就需要重新回归
	//注意:可以不需要if判断,没有超过k,obj->tail %= (obj->k + 1)仍为tail
	return true;
}

3、bool myCircularQueueDeQueue(MyCircularQueue* obj);出队列

//出队列成功,返回真,没有成功(队列为空),返回假
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
	if (myCircularQueueIsEmpty(obj))
	{
		return false;
	}

	++obj->front;//直接++,不遍历即可
	obj->front %= (obj->k + 1);//当删除的次数超过4次,也就是下标大于k,下标就需要重新回归
	//注意:可以不需要if判断
	return true;
}

4、int myCircularQueueFront(MyCircularQueue* obj);获取队头元素

//当队列为空,返回-1。否则返回队头数据。
int myCircularQueueFront(MyCircularQueue* obj)
{
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}
	return obj->a[obj->front];
}

5、int myCircularQueueRear(MyCircularQueue* obj);获取队尾元素

//当队列为空,返回-1。否则返回队尾数据。
int myCircularQueueRear(MyCircularQueue* obj)
{
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}
	//注意:返回队尾下标的前一个数据

	//方法1
	/*
	if (obj->tail == 0)//当队尾下标为0,则前一个数据是下标为k的值
	{
		return obj->a[obj->k];
	}
	else
	{
		return obj->a[obj->tail - 1];
	}
	*/

	//方法2
	int i = (obj->tail + obj->k) % (obj->k + 1);//获取真实下标
	return obj->a[i];
}

6、bool myCircularQueueIsEmpty(MyCircularQueue* obj);判断循环队列是否为空

bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
	return obj->front == obj->tail;
}

7、bool myCircularQueueIsFull(MyCircularQueue* obj);判断循环队列是否为满

bool myCircularQueueIsFull(MyCircularQueue* obj)
{
	return (obj->tail + 1) % (obj->k + 1) == obj->front;
}


8、void myCircularQueueFree(MyCircularQueue* obj);销毁循环队列

void myCircularQueueFree(MyCircularQueue* obj)
{
	free(obj->a);
	free(obj);
}

代码测试

int main()
{
	MyCircularQueue* cq = myCircularQueueCreate(4);
	
	myCircularQueueEnQueue(cq, 1);
	myCircularQueueEnQueue(cq, 2);
	myCircularQueueEnQueue(cq, 3);
	myCircularQueueEnQueue(cq, 4);
	
	while (!myCircularQueueIsEmpty(cq))
	{
		int front = myCircularQueueFront(cq);
		printf("%d ", front);
		myCircularQueueDeQueue(cq);
	}
	
	myCircularQueueFree(cq);
	return 0;
}

总结
循环队列,可以用数组实现,通过下标控制。也可以用链表实现,即循环链表。
1、当用数组实现时,下标front == tail表示空,(tail+1)%(K+1) == front表示满
2、当用链表实现时,指针front == tail表示空,tail->next == front表示满


你可能感兴趣的:(数据结构和算法,数据结构,链表,算法)