不一样的循环队列

起因

在九度ac了一道还算不错的队列题目,记录一下,数组实现的循环队列

题目描述:

大家都知道数据结构里面有一个结构叫做循环队列。顾名思义,这是一个队列,并且是循环的。但是现在,淘气的囧哥给这个循环队列加上了一些规矩,其中有5条指令:

(1)Push K, 让元素K进队列。

(2)Pop,对头元素出队列。

(3)Query K,查找队列中第K个元素,注意K的合法性

(4)Isempty,判断队列是否为空。

(5)Isfull,判断队列是否已满。

现在有N行指令,并且告诉你队列大小是M。

输入:

第一行包含两个整数N和M。1<=N,M<=100000。

接下来有N行,表示指令,指令格式见题目描述。

其中元素均在int范围。

输出:

对于指令(1),若队列已满,输出failed,否则不做输出。

对于指令(2),若队列已空,输出failed,否则不做输出。

对于指令(3),输出队列中第K个元素,若不存在,输出failed。

对于指令(4)和(5),则用yes或者no回答。

详情见样例。

样例输入:
12 2Push 1Push 2Push 3Query 2Query 3IsemptyIsfullPopPopPopIsemptyIsfull
样例输出:
failed2failednoyesfailedyesno

AC代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define queuesize 100001	//最大队列长度

struct queue
{
	int front;
	int rear;
	int data[queuesize];
	int count;	//记录队列中的元素
};

void InitQueue(struct queue *Q);
void EnQueue(struct queue *Q, int element, int m);
void Dequeue(struct queue *Q, int m);
void QueueSearch(struct queue *Q, int k, int m);

int main()
{
	int n, m, i, element, k, flag;
	char command[10];

	while(scanf("%d%d",&n, &m) != EOF)
	{
		if(n < 1 || m > 100000)
			return 0;
		struct queue *Q;
		Q = malloc(sizeof(struct queue));
		InitQueue(Q);
		for(i = 0; i < n; i ++)
		{
			scanf("%s",command);
			if (strcmp(command,"Push") == 0)
			{
				scanf("%d",&element);
				EnQueue(Q, element, m);
			}else if (strcmp(command,"Pop") == 0)
			{
				Dequeue(Q, m);
			}else if (strcmp(command,"Query") == 0)
			{
				scanf("%d",&k);
				QueueSearch(Q, k, m);
			}else if (strcmp(command,"Isempty") == 0)
			{
				flag = (Q -> count == 0)? 1 : 0;
				if(flag)
				{
					printf("yes\n");
				}else
				{
					printf("no\n");
				}
			}else if (strcmp(command,"Isfull") == 0)
			{
				flag = (Q -> count == m)? 1 : 0;
				if(flag)
				{
					printf("yes\n");
				}else
				{
					printf("no\n");
				}
			}
		}
	}	
	return 0;
}

/**
 * Description:队列初始化
 */
void InitQueue(struct queue *Q)
{
	Q -> front = Q -> rear = 0;
	Q -> count = 0;
}

/**
 * Description:入队操作
 */
void EnQueue(struct queue *Q, int element, int m)
{
	int flag;
	flag = (Q -> count == m)? 1 : 0;	

	if(!flag)
	{
		Q -> data[Q -> rear] = element;
		Q -> count ++;
		Q -> rear = (Q -> rear + 1) % m;
	}else
	{
		printf("failed\n");
	}
}

/**
 * Description:出队操作
 */
void Dequeue(struct queue *Q, int m)
{
	int flag;
	int element;

	flag = (Q -> count == 0)? 1 : 0;

	if(!flag)
	{
		element = Q -> data[Q -> front];
		Q -> front = (Q -> front + 1) % m;
		Q -> count --;
	}else
	{
		printf("failed\n");
	}
}

/**
 * Description:查找队列中的指定元素
 */
void QueueSearch(struct queue *Q, int k, int m)
{
	int flag, temp;
	flag = (Q -> count == 0)? 1: 0;
	temp = Q -> front + k - 1;
	if((!flag) && (k <= m && k >= 1))
	{
		if((Q -> front < Q -> rear) && ( Q-> front <= temp && Q -> rear > temp))
			printf("%d\n",Q -> data[temp]);
		else if((Q -> front > Q -> rear) && (temp >= Q -> front || temp < Q->rear))
			printf("%d\n",Q -> data[temp]);
		else if(Q -> front == Q -> rear)
			printf("%d\n",Q -> data[temp]);
		else
			printf("failed\n");
	}else
	{
		printf("failed\n");
	}
}


<think>嗯,我现在要了解循环队列是如何实现的。首先,循环队列是什么?好像是为了解决普通队列在出队后空间无法再利用的问题,对吧?那普通队列的数组实现中,当队尾到达数组末尾时,即使前面有空位,也无法再插入新元素,这样会导致空间浪费。循环队列通过把数组的头尾相接,形成一个环状结构,可以重复利用出队元素留下的空间。 那循环队列具体怎么实现呢?应该还是用数组或者链表来存储元素吧?可能数组更常见一些,因为需要计算下标的位置。然后需要两个指针,一个指向队头(front),一个指向队尾(rear),或者用两个变量来记录它们的索引位置。比如,初始时front和rear都指向0的位置。 当插入元素时,rear指针会向后移动,但如果是循环的话,当到达数组末尾时,下一个位置应该是数组的开头。所以计算rear的时候要用模运算,比如(rear + 1) % capacity。这样就能循环了。同样,出队的时候,front指针也是用模运算来移动。 循环队列的一个关键问题是如何判断队列是否已满或者为空。因为当队列满的时候,rear和front的位置可能看起来和空队列的情况一样。比如,假设队列的容量是5,当front和rear都指向0时,队列为空;而当插入5个元素后,rear可能又回到0的位置,这时候front也是0,所以这时候队列满和空的状态可能无法区分。 那解决方法是什么呢?常见的方法可能是留一个空位,这样当rear的下一个位置是front时,就认为队列已满。也就是说,队列的实际容量是数组长度减一。比如,数组长度是n,那么队列最多可以存储n-1个元素。这样,当(rear + 1) % capacity == front的时候,队列已满,而当rear == front的时候,队列为空。 另外,可能需要维护一个容量变量或者通过计算来确定队列中的元素数量。过使用上述方法的话,可能需要,直接通过front和rear的位置关系来判断空和满的情况。 那具体的实现步骤大概是这样的: 1. 定义一个固定大小的数组,capacity为数组长度,实际可用空间是capacity - 1。 2. 初始化front和rear都为0。 3. 入队操作时,先检查是否队列已满。如果满了则无法插入,否则将元素放在rear的位置,然后rear = (rear + 1) % capacity。 4. 出队操作时,检查是否队列为空。如果为空则无法出队,否则取出front位置的元素,然后front = (front + 1) % capacity。 5. 判断队列是否为空的条件是front == rear。 6. 判断队列是否已满的条件是(rear + 1) % capacity == front。 过这里可能需要注意一些边界情况,比如当capacity为0的情况,或者初始化时数组的大小应该怎么处理。比如用户指定一个容量,实际创建的数组长度是容量加一吗?或者容量参数就是数组的实际长度,而可用空间是容量减一? 比如,用户想要一个容量为k的循环队列,那么数组的长度应该是k + 1,这样可用空间是k。或者可能用户传入的数组大小即为可用容量,此时在内部数组需要是用户传入的大小加一? 这部分可能需要具体实现的时候考虑清楚。例如,构造函数可能接收一个容量参数,然后内部数组初始化为capacity + 1的大小,这样可用空间就是capacity。 另外,队列中的元素数量可以通过(rear - front + capacity) % capacity来计算,这样可以方便地获取当前队列中有多少元素。但有时候可能需要这个计算,只要满足空和满的条件即可。 还有一个问题,当数组被填满的时候,是否需要进行动态扩容?这可能要看具体应用场景,如果是固定大小的循环队列,那么需要扩容,满了就拒绝入队。如果是可扩容的,可能需要重新分配更大的数组,并将元素复制过去。过这会增加实现的复杂度,可能大多数情况下循环队列都是固定大小的。 总结一下,循环队列的实现步骤: - 使用数组存储元素,并维护front和rear指针。 - 通过模运算来处理指针的移动,实现循环。 - 利用预留一个空位的方法来区分队列满和空的情况。 - 入队和出队操作时检查队列的状态(是否满或空)。 可能还需要考虑其他操作,比如获取队首元素,检查队列大小等等。这些都可以基于front和rear的位置进行计算。 最后,可能需要用代码示例来具体说明。比如用Java或者Python来实现循环队列的结构。例如,Python中可以用列表模拟数组,front和rear的移动通过模运算处理。过要注意列表的大小和索引的处理。</think>循环队列通过环形数组和两个指针(`front` 和 `rear`)实现,解决了普通队列空间浪费的问题。以下是逐步实现的关键点: --- ### **1. 核心思想** - **环形结构**:将数组视为环形,利用**模运算**实现指针循环移动。 - **空位预留**:数组中预留一个空位,用于区分队列“满”和“空”的状态。 --- ### **2. 关键变量** - **数组**:存储元素,长度为 `capacity`(实际可用容量为 `capacity - 1`)。 - **`front`**:指向队首元素。 - **`rear`**:指向队尾的下一个空位。 --- ### **3. 操作实现** #### **(1) 初始化** ```python class CircularQueue: def __init__(self, capacity): self.capacity = capacity + 1 # 实际数组长度比可用容量大1 self.queue = [None] * self.capacity self.front = 0 self.rear = 0 ``` --- #### **(2) 判空判满** - **判空**:`front == rear` - **判满**:`(rear + 1) % capacity == front` ```python def is_empty(self): return self.front == self.rear def is_full(self): return (self.rear + 1) % self.capacity == self.front ``` --- #### **(3) 入队操作** 1. 检查队列是否已满。 2. 插入元素到 `rear` 位置。 3. `rear` 后移一位(取模)。 ```python def enqueue(self, item): if self.is_full(): raise Exception("队列已满") self.queue[self.rear] = item self.rear = (self.rear + 1) % self.capacity ``` --- #### **(4) 出队操作** 1. 检查队列是否为空。 2. 取出 `front` 位置的元素。 3. `front` 后移一位(取模)。 ```python def dequeue(self): if self.is_empty(): raise Exception("队列为空") item = self.queue[self.front] self.front = (self.front + 1) % self.capacity return item ``` --- ### **4. 计算元素数量** 通过 `front` 和 `rear` 的位置计算当前元素数: ```python def size(self): return (self.rear - self.front + self.capacity) % self.capacity ``` --- ### **5. 示例演示** 假设队列容量为 3(数组长度为 4): 1. 初始化:`front = 0`, `rear = 0`(队列空)。 2. 入队 A:`rear = 1`。 3. 入队 B:`rear = 2`。 4. 入队 C:`rear = 3`,此时 `(3 + 1) % 4 = 0 == front`,队列满。 5. 出队 A:`front = 1`,空出位置 0。 6. 入队 D:`rear = 0`,成功插入。 --- ### **6. 动态扩容(可选)** 若需支持动态扩容,可在队列满时: 1. 创建新数组(容量翻倍)。 2. 将旧数组元素按顺序复制到新数组。 3. 重置 `front` 和 `rear`。 --- ### **总结** - **优点**:空间利用率高,入队/出队时间复杂度为 $O(1)$。 - **关键点**:通过模运算和预留空位管理指针,区分队列状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值