队列练习之Example004-设计一个循环队列,用 front 和 rear 分别作为队头和队尾指针,另外用一个标志 tag 表示队列是空还是不空
Posted 二木成林
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了队列练习之Example004-设计一个循环队列,用 front 和 rear 分别作为队头和队尾指针,另外用一个标志 tag 表示队列是空还是不空相关的知识,希望对你有一定的参考价值。
Example004
题目
设计一个循环队列,用 front 和 rear 分别作为队头和队尾指针,另外用一个标志 tag 表示队列是空还是不空,约定当 tag 为 0 时队空,当 tag 为 1 时队不空,这样就可以用 front==rear
作为队满的条件。要求,设计队列的结构和相关基本运算算法(队列元素为 int 型)。
分析
在循环队列中,提供的代码中是占用一个数组空间用来判断是否队满,其实还有其他判断队满的方式,以下就是一种:增设一个 tag 整型变量来标识队列是否为空,如果 tag 为 0 标识队空,如果为 1 则表示队满。在入队和出队算法中,在入队时将 tag 置为 1,因为只有入队操作可能导致队满;在出队时将 tag 置为 0,因为只有出队操作可能导致队空。关于使用 tag 标记后循环队列 queue 的操作如下:
- 初始化:
queue.tag=0; queue.front=queue.rear=0;
。 - 队空条件:
queue.front==queue.rear&&queue.tag==0
。 - 队满条件:
queue.front==queue.rear&&queue.tag==1
。 - 入队操作:
queue.data[queue.rear]=ele; queue.rear=(queue.rear+1)%MAXSIZE; queue.tag=1;
。 - 出队操作:
ele=queue.data[queue.front]; queue.front=(queue.front+1)%MAXSIZE; queue.tag=0;
。
注意:
- 在使用 tag 标记的循环队列中,数组如果最大长度为
MAXSIZE
,那么最多能存储MAXSIZE
个元素。而在提供的循环队列代码中,专门占用了一个数组空间用来判断是否队满,所以最多只能存储MAXSIZE-1
个元素。 - 队头指针
front
指向队头元素。队尾指针指向队尾元素的下一个位置。
图解
- 初始化:
- 队空:
- 队非满:
- 队满
- 入队
- 出队
C实现
核心代码:
/**
* 将元素入队
* @param queue 循环队列
* @param ele 待入队的元素
* @return 如果返回 1 表示入队成功,否则返回 0 表示入队失败
*/
int enQueue(CircularQueue *queue, int ele)
// 0.参数校验,如果队满则返回 0
if (queue->front == queue->rear && queue->tag == 1)
return 0;
// 1.将元素入队
// 1.1 由于队尾指针指向队尾元素的下一个位置,所以直接填充
queue->data[queue->rear] = ele;
// 1.2 填充后,修改队尾指针,将其加一,由于是循环队列,所以要对 MAXSIZE 进行取余
queue->rear = (queue->rear + 1) % MAXSIZE;
// 1.3 修改标记 tag,只要有元素入队,那么队列就会非空,所以置为 1
queue->tag = 1;
// 1.4 返回 1 表示入队成功
return 1;
/**
* 将元素出队
* @param queue 循环队列
* @param ele 用来保存出队的元素
* 如果返回 1 表示出队成功,否则返回 0 表示出队失败
*/
int deQueue(CircularQueue *queue, int *ele)
// 0.参数校验,如果为空则不能出队返回 0
if (queue->front == queue->rear && queue->tag == 0)
return 0;
// 1.将元素出队
// 1.1 保存队头指针所指向的队头元素
*ele = queue->data[queue->front];
// 1.2 修改队头指针,将其加一,表示删除原队头元素,但由于是循环队列,所以要对 MAXSIZE 取余
queue->front = (queue->front + 1) % MAXSIZE;
// 1.3 修改标记 tag,出队后队列可能为空,所以将标记置为 0
queue->tag = 0;
// 1.4 返回 1 表示出队成功
return 1;
完整代码:
#include<stdio.h>
/**
* 循环队列中能存储的最大元素个数
*/
#define MAXSIZE 5
/**
* 循环队列结构体定义
*/
typedef struct
/**
* 循环队列数据域,存储队列中的元素
*/
int data[MAXSIZE];
/**
* 指针域,存储队头元素的下标
*/
int front;
/**
* 指针域,存储队尾元素的下标
*/
int rear;
/**
* 标志,表示队列是空还是不空,约定当 tag 为 0 时队空,当 tag 为 1 时队不空
*/
int tag;
CircularQueue;
/**
* 初始化循环队列
* @param queue 未初始化的循环队列
*/
void init(CircularQueue *queue)
// 初始化队头和队尾指针,都指向 0
queue->front = 0;
queue->rear = 0;
// 将标志置为 0 表示空队
queue->tag = 0;
/**
* 判断循环队列是否为空队列
* @param queue 循环队列
* @return 如果是空队列则返回 1,否则返回 0
*/
int isEmpty(CircularQueue queue)
// 因为标记 tag 用来存储队列是否为空,所以判断标记是否为零即可
return queue.front == queue.rear && queue.tag == 0;
/**
* 判断循环队列是否为满
* @param queue 循环队列
* @return 如果是满队列则返回 1,否则返回 0
*/
int isFull(CircularQueue queue)
// 注意,此队列不需要一个额外的空间来判断是否满,所以判断队满的标志是队头指针与队尾指针指向同一个位置
// 但前提是队列非空,如果队列为空,那么它们也是指向同一个位置的,所以要加上这个条件
return queue.front == queue.rear && queue.tag == 1;
/**
* 将元素入队
* @param queue 循环队列
* @param ele 待入队的元素
* @return 如果返回 1 表示入队成功,否则返回 0 表示入队失败
*/
int enQueue(CircularQueue *queue, int ele)
// 0.参数校验,如果队满则返回 0
if (queue->front == queue->rear && queue->tag == 1)
return 0;
// 1.将元素入队
// 1.1 由于队尾指针指向队尾元素的下一个位置,所以直接填充
queue->data[queue->rear] = ele;
// 1.2 填充后,修改队尾指针,将其加一,由于是循环队列,所以要对 MAXSIZE 进行取余
queue->rear = (queue->rear + 1) % MAXSIZE;
// 1.3 修改标记 tag,只要有元素入队,那么队列就会非空,所以置为 1
queue->tag = 1;
// 1.4 返回 1 表示入队成功
return 1;
/**
* 将元素出队
* @param queue 循环队列
* @param ele 用来保存出队的元素
* 如果返回 1 表示出队成功,否则返回 0 表示出队失败
*/
int deQueue(CircularQueue *queue, int *ele)
// 0.参数校验,如果为空则不能出队返回 0
if (queue->front == queue->rear && queue->tag == 0)
return 0;
// 1.将元素出队
// 1.1 保存队头指针所指向的队头元素
*ele = queue->data[queue->front];
// 1.2 修改队头指针,将其加一,表示删除原队头元素,但由于是循环队列,所以要对 MAXSIZE 取余
queue->front = (queue->front + 1) % MAXSIZE;
// 1.3 修改标记 tag,出队后队列可能为空,所以将标记置为 0
queue->tag = 0;
// 1.4 返回 1 表示出队成功
return 1;
/**
* 打印循环队列所有元素,从队头到队尾
* @param queue 循环队列
*/
void print(CircularQueue queue)
// 如果是空队列则单独打印
if (queue.front == queue.rear && queue.tag == 0)
printf("[]\\n");
return;
// 如果是非空队列
printf("[");
int front = queue.front;
// 注意这里使用的的 do...while 循环,因为满队时,队头指针和队尾指针指向同一个位置,所以要先打印一个值再启动循环
do
printf("%d", queue.data[front]);
if (front != (queue.rear - 1 + MAXSIZE) % MAXSIZE)
printf(", ");
front = (front + 1) % MAXSIZE;
while (front != queue.rear);
printf("]\\n");
int main()
// 声明循环队列
CircularQueue queue;
// 初始化队列
printf("\\n初始化队列:\\n");
init(&queue);
print(queue);
// 队列是否空
printf("\\n队列是否空:\\n");
int empty;
empty = isEmpty(queue);
printf("%d\\n", empty);
// 将元素入队
printf("\\n将元素入队:\\n");
enQueue(&queue, 11);
print(queue);
enQueue(&queue, 22);
print(queue);
enQueue(&queue, 33);
print(queue);
enQueue(&queue, 44);
print(queue);
enQueue(&queue, 55);
print(queue);
// 队列是否满
printf("\\n队列是否满:\\n");
int full;
full = isFull(queue);
printf("%d\\n", full);
// 将元素出队
int ele;
printf("\\n将元素出队:\\n");
deQueue(&queue, &ele);
printf("出队元素:%d\\n", ele);
print(queue);
deQueue(&queue, &ele);
printf("出队元素:%d\\n", ele);
print(queue);
deQueue(&queue, &ele);
printf("出队元素:%d\\n", ele);
print(queue);
// 队列是否空
printf("\\n队列是否空:\\n");
empty = isEmpty(queue);
printf("%d\\n", empty);
执行结果:
初始化队列:
[]
队列是否空:
1
将元素入队:
[11]
[11, 22]
[11, 22, 33]
[11, 22, 33, 44]
[11, 22, 33, 44, 55]
队列是否满:
1
将元素出队:
出队元素:11
[22, 33, 44, 55]
出队元素:22
[33, 44, 55]
出队元素:33
[44, 55]
队列是否空:
0
Java实现
核心代码:
/**
* 将元素入队
*
* @param ele 待入队的元素
* @throws Exception 如果队列已满则不能入队则抛出此异常
*/
public void enQueue(int ele) throws Exception
// 0.参数校验,如果队满则抛出异常表示不能入队
if (isFull())
throw new Exception("队列已满不能入队!");
// 1.将元素入队
// 1.1 由于队尾指针指向队尾元素的下一个位置,所以直接填充
queue.data[queue.rear] = ele;
// 1.2 填充后,修改队尾指针,将其加一,由于是循环队列,所以要对 MAXSIZE 进行取余
queue.rear = (queue.rear + 1) % MAXSIZE;
// 1.3 修改标记 tag,只要有元素入队,那么队列就会非空,所以置为 1
queue.tag = 1;
/**
* 将元素出队
*
* @return 出队元素
* @throws Exception 如果队空则不能出队抛出此异常
*/
public int deQueue() throws Exception
// 0.参数校验,如果为空则不能出队抛出此异常
if (isEmpty())
throw new Exception("队列为空不能出队!");
// 1.将元素出队
// 1.1 保存队头指针所指向的队头元素
int data = queue.data[queue.front];
// 1.2 修改队头指针,将其加一,表示删除原队头元素,但由于是循环队列,所以要对 MAXSIZE 取余
queue.front = (queue.front + 1) % MAXSIZE;
// 1.3 修改标记 tag,出队后队列可能为空,所以将标记置为 0。
queue.tag = 0;
// 1.4 返回队头元素
return data;
完整代码:
public class CircularQueue
/**
* 循环队列中能存储的最大元素个数
*/
private final int MAXSIZE = 5;
/**
* 声明一个循环队列
*/
private Queue queue;
/**
* 初始化循环队列
*/
public void init()
// 实例化对象
queue = new Queue();
// 为数据域分配存储空间
queue.data = new int[MAXSIZE];
// 将队头指针和队尾指针都指向 0
queue.front = 0;
queue.rear = 0;
// 将标记置为 0,表示空队列
queue.tag = 0;
/**
* 判断循环队列是否为空队列
*
* @return 如果是空队列则返回 true,否则返回 false
*/
public boolean isEmpty()
// 因为标记 tag 用来存储队列是否为空,所以判断标记是否为零即可
return queue.front == queue.rear && queue.tag == 0;
/**
* 判断循环队列是否为满
*
* @return 如果是满队列则返回 true,否则返回 false
*/
public boolean isFull()
// 注意,此队列不需要一个额外的空间来判断是否满,所以判断队满的标志是队头指针与队尾指针指向同一个位置
// 但前提是队列非空,如果队列为空,那么它们也是指向同一个位置的,所以要加上这个条件
return queue.front == queue.rear && queue.tag == 1;
/**
* 将元素入队
*
* @param ele 待入队的元素
* @throws Exception 如果队列已满则不能入队则抛出此异常
*/
public void enQueue(int ele) throws Exception
// 0.参数校验,如果队满则抛出异常表示不能入队
if (isFull())
throw new Exception("队列已满不能入队!");
// 1.将元素入队
// 1.1 由于队尾指针指向队尾元素的下一个位置,所以直接填充
queue.data[queue.rear] = ele;
// 1.2 填充后,修改队尾指针,将其加一,由于是循环队列,所以要对 MAXSIZE 进行取余
queue.rear = (queue.rear + 1) % MAXSIZE;
// 1.3 修改标记 tag,只要有元素入队,那么队列就会非空,所以置为 1
queue.tag = 1;
/**
* 将元素出队
*
* @return 出队元素
* @throws Exception 如果队空则不能出队抛出此异常
*/
public int deQueue() throws Exception
// 0.参数校验,如果为空则不能出队抛出此异常
if (isEmpty())
throw new Exception("队列为空不能出队!");
// 1.将元素出队
// 1.1 保存队头指针所指向的队头元素
int data = queue.data[queue.front];
// 1.2 修改队头指针,将其加一,表示删除原队头元素,但由于是循环队列,所以要对 MAXSIZE 取余
queue.front = (queue.front + 1) % MAXSIZE;
// 1.3 修改标记 tag,出队后队列可能为空,所以将标记置为 0。
queue.tag = 0;
// 1.4 返回队头元素
return data;
/**
* 打印循环队列所有元素,从队头到队尾
*/
public void print()
// 如果是空队列则单独打印
if (queue.front == queue.rear && queue.tag == 0)
System.out.println("[]");
return;
// 如果是非空队列
System.out.print("[");
int front = queue.front;
// 注意这里使用的的 do...while 循环,因为满队时,队头指针和队尾指针指向同一个位置,所以要先打印一个值再启动循环
do
System.out.print(queue.data[front]);
if (front != (queue.rear - 1 + MAXSIZE) % MAXSIZE)
System.out.print(", ");
front = (front + 1) % MAXSIZE;
while (front != queue.rear);
System.out.print("]\\n");
/**
* 循环队列定义
*/
class Queue
/**
* 循环队列数据域,存储队列中的元素
*/
int[] data;
/**
* 指针域,存储队头元素的下标
*/
int front;
/**
* 指针域,存储队尾元素的下标
*/
int rear;
/**
* 标志,表示队列是空还是不空,约定当 tag 为 0 时队空,当 tag 为 1 时队不空
*/
int tag;
测试代码:
public class CircularQueueTest
public static void main(String[] args) throws Exception
// 声明循环队列
CircularQueue queue = new CircularQueue();
// 初始化队列
System.out.print("\\n初始化队列:\\n");
queue.init();
queue.print();
// 队列是否空
System.out.print("\\n队列是否空:\\n");
boolean empty;
empty = queue.isEmpty();
System.out.println(empty);
// 将元素入队
System.out.print("\\n将元素入队:\\n");
queue.enQueue(11);
queue.print();
queue.enQueue(22);
queue.print();
queue.enQueue(33);
queue.print();
queue.enQueue(44);
queue.print();
queue.enQueue(55);
queue.print();
// 队列是否满
System.out.print("\\n队列是否满:\\n");
boolean full;
full = queue.isFull();
System.out.println(full);
// 将元素出队
int ele;
System.out.print("\\n将元素出队:\\n");
ele = queue.deQueue();
System.out.println("出队元素:" + ele);
queue.print();
ele = queue.deQueue();
System.out.println("出队元素:" + ele);
queue.print();
ele = queue.deQueue();
System<以上是关于队列练习之Example004-设计一个循环队列,用 front 和 rear 分别作为队头和队尾指针,另外用一个标志 tag 表示队列是空还是不空的主要内容,如果未能解决你的问题,请参考以下文章
队列练习之Example006-设计队列要求入队时增加队列空间,出队后出队元素所占用空间可重复使用,以保持队列空间只增不减,并且要求入队操作和出队操作的时间复杂度都为O
队列练习之Example001-用两个栈 s1 和 s2 来模拟一个队列,实现队列的出队入队队是否为空的运算
队列练习之Example005-Q 是一个队列,S 是一个空栈,实现将队列中的元素逆置的算法
栈练习之Example004-顺序栈 s0 和 s1 共享一个存储区 elem,设计共享栈关于入栈和出栈操作的算法
队列练习之Example002-用带头结点的循环链表表示队列,并且只设一个指针指向队尾结点,实现对应的入队列和出队列的算法