赞
踩
目录
将顺序存储队列的元素的一维数组首尾相接,形成一个环状,如下图所示,这种形式表示的队列称为循环队列。循环队列仍然是顺序队列结构,只是逻辑上和前面的顺序队列有所不同。
- #define MAXLEN 6 // 定义环形队列的最大长度为 6
-
- typedef int DataType; // 定义数据类型为整型
-
- typedef struct CircularQueue // 定义环形队列的结构体
- {
- DataType a[MAXLEN]; // 定义存储数据的数组
- int front, rear; // 定义队头和队尾指针
- int size; // 定义队列元素个数
- } CQueue;
-
- void InitCQueue(CQueue* q) // 初始化环形队列
- {
- q->front = q->rear = 0; // 队头和队尾指针都指向队列的开始位置
- q->size = 0; // 队列元素个数为 0,即初始为空队列
- }
在数据结构中,可以使用一个 front 指针和一个 rear 指针来表示环状队列的队头和队尾位置,当 rear 指针移动到数组的最后一个位置时,如果再有元素需要入队,那么应该将 rear 指针指向数组的第一个位置。同样地,当 front 指针移动到数组的最后一个位置时,如果还有元素需要出队,那么应该将 front 指针指向数组的第一个位置。
具体实现方法如下:
初始化:定义一个数组和两个指针 front 和 rear,初始化时,将 front 指针和 rear 指针都指向数组的第一个位置。
入队:如果队列未满,则将元素插入 rear 指向的位置,然后将 rear 指针后移一位。当 rear 指针移动到数组的最后一个位置时,若队列未满,则将 rear 指针指向数组的第一个位置。
出队:如果队列非空,则将队头元素取出,然后将 front 指针后移一位。当 front 指针移动到数组的最后一个位置时,只要队列非空,就将 front 指针指向数组的第一个位置。
假设队列开辟的数组单元数为MAXSIZE,它的数组下标在0~MAXSIZE-1之间,若使队头或队尾增1,且使front和rear指针对应的数组下标保持在数组范围内,可以利用取模运算实现。
例如,在下图所示的循环队列示意图最大空间为MAXSIZE=8,数组下标为0~7之间。
非空队时如图(2)中队头指针front指向队列中队头元素的前一个位置,队尾指针rear 指向队列的队尾元素位置。
入队代码实现:
- void CQueuePush(CQueue* q, DataType x) // 元素入队
- {
- assert(q); // 判断 q 是否为空
- if (!CQueueFull(q)) // 如果队列未满
- {
- q->rear = (q->rear + 1) % MAXLEN; // 队尾指针后移一位
- q->a[q->rear] = x; // 在队尾处添加元素
- q->size++; // 队列元素个数加 1
- }
- else // 队列已满,无法添加数据
- {
- printf("队列已满,无法添加数据!\n");
- exit(-1);
- }
- }
出队代码实现:
- void CQueuePop(CQueue* q) // 元素出队
- {
- assert(q); // 判断 q 是否为空
- if (!CQueueEmpty(q)) // 如果队列非空
- {
- q->front = (q->front + 1) % MAXLEN; // 队头指针后移一位
- q->size--; // 队列元素个数减 1
- }
- else // 队列已空,无法删除数据
- {
- printf("队列已空,无法删除数据!\n");
- exit(-1);
- }
- }
图(1)中队头与队尾指针指向同一位置时为空队,判断方法与顺序队列一致。
代码实现:
- int CQueueEmpty(CQueue* q) // 判断队列是否为空
- {
- assert(q); // 判断 q 是否为空
- if (q->front == q->rear) // 通过队头和队尾指针是否相等,判断队列是否为空
- {
- return 1; // 队列为空
- }
- return 0; // 队列非空
- }
由 图(3) 可见,循环队列解决了顺序队列中“假溢出”的现象,充分利用了固定长度的队列中的空间。我们知道,在长度不可增长的顺序队列中,判断队列是否队满的条件是rear==MAXLEN。那么在循环队列中,我们判断队满的方式则为:(rear+1)%MAXLEN==front;
代码实现:
- int CQueueFull(CQueue* q) // 判断队列是否为满
- {
- assert(q); // 判断 q 是否为空
- if ((q->rear + 1) % MAXLEN == q->front) // 通过队尾和队头指针是否相邻,判断队列是否为满
- {
- return 1; // 队列为满
- }
- return 0; // 队列未满
- }
我们理解完顺序队列与循环队列的差异后,在固定长度代码的基础上对front、rear指针的移动和判满操作进行修改即可得到循环队列的代码。
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
-
-
- #define MAXLEN 6 // 定义环形队列的最大长度为 6
-
- typedef int DataType; // 定义数据类型为整型
-
- typedef struct CircularQueue // 定义环形队列的结构体
- {
- DataType a[MAXLEN]; // 定义存储数据的数组
- int front, rear; // 定义队头和队尾指针
- int size; // 定义队列元素个数
- } CQueue;
-
- void InitCQueue(CQueue* q) // 初始化环形队列
- {
- q->front = q->rear = 0; // 队头和队尾指针都指向队列的开始位置
- q->size = 0; // 队列元素个数为 0,即初始为空队列
- }
-
- int CQueueFull(CQueue* q) // 判断队列是否为满
- {
- assert(q); // 判断 q 是否为空
- if ((q->rear + 1) % MAXLEN == q->front) // 通过队尾和队头指针是否相邻,判断队列是否为满
- {
- return 1; // 队列为满
- }
- return 0; // 队列未满
- }
-
- int CQueueEmpty(CQueue* q) // 判断队列是否为空
- {
- assert(q); // 判断 q 是否为空
- if (q->front == q->rear) // 通过队头和队尾指针是否相等,判断队列是否为空
- {
- return 1; // 队列为空
- }
- return 0; // 队列非空
- }
-
- void CQueuePush(CQueue* q, DataType x) // 元素入队
- {
- assert(q); // 判断 q 是否为空
- if (!CQueueFull(q)) // 如果队列未满
- {
- q->rear = (q->rear + 1) % MAXLEN; // 队尾指针后移一位
- q->a[q->rear] = x; // 在队尾处添加元素
- q->size++; // 队列元素个数加 1
- }
- else // 队列已满,无法添加数据
- {
- printf("队列已满,无法添加数据!\n");
- exit(-1);
- }
- }
-
- void CQueuePop(CQueue* q) // 元素出队
- {
- assert(q); // 判断 q 是否为空
- if (!CQueueEmpty(q)) // 如果队列非空
- {
- q->front = (q->front + 1) % MAXLEN; // 队头指针后移一位
- q->size--; // 队列元素个数减 1
- }
- else // 队列已空,无法删除数据
- {
- printf("队列已空,无法删除数据!\n");
- exit(-1);
- }
- }
-
- int CQueueTop(CQueue* q) // 获取队首元素
- {
- if (!CQueueEmpty(q)) // 如果队列非空
- {
- return q->a[q->front + 1]; // 返回队头下一个位置的元素
- }
- else // 队列已空,无法获取队首数据
- {
- printf("队列已空,无法获取队首数据!\n");
- exit(-1);
- }
- }
-
- int CQueueTail(CQueue* q) // 获取队尾元素
- {
- if (!CQueueEmpty(q)) // 如果队列非空
- {
- return q->a[q->rear]; // 返回队尾位置的元素
- }
- else // 队列已空,无法获取队尾数据
- {
- printf("队列已空,无法获取队尾数据!\n");
- exit(-1);
- }
- }
-
-
- int main()
- {
- CQueue q;
- InitCQueue(&q);
- CQueuePush(&q, 1);
- CQueuePush(&q, 2);
- CQueuePush(&q, 3);
- CQueuePush(&q, 4);
- CQueuePush(&q, 5);
- CQueuePop(&q);
- CQueuePop(&q);
- CQueuePop(&q);
- CQueuePop(&q);
- //CQueuePop(&q);
- int x;
- x = CQueueTop(&q);
- printf("%d\n", x);
- x = CQueueTail(&q);
- printf("%d\n", x);
- return 0;
- }
循环队列通过环形数组的设计,充分利用了存储空间,并实现了高效的元素入队和出队操作。在使用循环队列时,需要特别注意队列为空和队列满的判断,避免出现错误。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。