循环队列概述及C语言实现

    所有的ADT都必须确定一件事情:如何分配内存来存储值,也就是顺序存储还是链式存储。顺序存储分为静态数组和动态分配的数组。就队列而言,顺序分配的队列需要在队列实现之前确定整个队列的长度,内存分配以数组方式实现;而链式队列无需指定队列长度,只要内存充足可以随时增加队列。为了在连续分配的有限内存中使用队列,需要将队列曾经释放的内存空间循环利用起来,因此产生了循环队列。循环队列避免了常规队列中使用数组的空间浪费。

1. 循环队列如何工作

在这里插入图片描述
    如上图所示,front指针指向队列的首部,rear指针指向队列的尾部。每新增一个元素,front指针不变,rear指针向后移动一位;每弹出一个元素,front指针向后移动一位,rear指针不变。经过一段时间的排队和出列,队列0,1索引对应的空间已经出现了空闲,队列元素数量已经减小。如果没有循环队列,只有当所有元素都已出队列,重置队列后才能使用索引0和1。
    循环队列的工作过程是循环递增的,即当队列末尾没有空间时,将变量添加到队列的开始。假设队列共有8个空间,新增1个元素的过程分为两步:1、rear指向下一个索引,其计算公式为 REAR=(REAR + 1)%8;2、将数值放到索引对应的内存空间。
在这里插入图片描述

2. 队列空与满的判断

    当front与rear指向同一个空间时,有两种可能:队列空或者队列满,所以无法根据这种情况判断队列的空与满。常见的判断队列的空与满有两种思路:1、引入一个新变量,用于记录队列中的元素数量;2、重新定义队列的 " 空 " 与 " 满 "。

2.1 引入新变量判断队列的空与满

    队列主要接口函数只有4个:isFull()用于判断队列是否满;isEmpty()用于判断队列是否空;enQueue()用于将新元素压入队列末尾;deQueue()用于将队列首部的元素弹出。假设循环队列元素数量为8,number变量负责表示队列元素的数量,队列状态变化过程如下表:

操作步骤初始状态插入一个元素删除一个元素连续插入元素倒数第二次插入元素最后一次插入元素
front00111
rear-10070
number01078
队列状态不空不满不空不满

    将代码保存为queue.addvar.c文件,用于后续验证,具体如下:

// Circular Queue implementation in C

#include <stdio.h>

#define SIZE 8

int items[SIZE];
int front = 0, rear = -1;
int number=0;

// Check if the queue is full
int isFull() {
  if ( number == SIZE ) return 1;
  return 0;
}

// Check if the queue is empty
int isEmpty() {
  if ( number == 0 ) return 1;
  return 0;
}

// Adding an element
void enQueue(int element) {
  if (isFull())
    printf("\n Queue is full!! \n");
  else {
    rear = (rear + 1) % SIZE;
    items[rear] = element;
    number++;
    printf("\n Inserted -> %d", element);
  }
}

// Removing an element
int deQueue() {
  int element;
  if (isEmpty()) {
    printf("\n Queue is empty !! \n");
    return (-1);
  } else {
    element = items[front];
    front = (front + 1) % SIZE;
    number--;
    printf("\n Deleted element -> %d \n", element);
    return (element);
  }
}

// Display the queue
void display() {
  int i;
  if (isEmpty())
    printf(" \n Empty Queue\n");
  else {
    printf("\n Front -> %d ", front);
    printf("\n Items -> ");
    for (i = front; i != rear; i = (i + 1) % SIZE) {
      printf("%d ", items[i]);
    }
    printf("%d ", items[i]);
    printf("\n Rear -> %d \n", rear);
  }
}
2.2 重新定义队列的空与满

    简单画了一下队列的空与满的状态图,队列空时,rear向前移1位就等于front,如下图所示:
在这里插入图片描述
    队列满时,rear向前移2位等于front,如下图所示:
在这里插入图片描述
    可以看到,使用该方法会浪费1个单位的空间。talk is cheap,保存代码为queue.define.c,用于后续验证,代码内容如下:

// Circular Queue implementation in C

#include <stdio.h>

#define SIZE 8

int items[SIZE];
int front = 1, rear = 0;

// Check if the queue is full
int isFull() {
  if ( front == (rear + 2) % SIZE ) return 1;
  return 0;
}

// Check if the queue is empty
int isEmpty() {
  if ( front == (rear + 1) % SIZE ) return 1;
  return 0;
}

// Adding an element
void enQueue(int element) {
  if (isFull())
    printf("\n Queue is full!! \n");
  else {
    rear = (rear + 1) % SIZE;
    items[rear] = element;
    printf("\n Inserted -> %d", element);
  }
}

// Removing an element
int deQueue() {
  int element;
  if (isEmpty()) {
    printf("\n Queue is empty !! \n");
    return (-1);
  } else {
    element = items[front];
    front = (front + 1) % SIZE;
    printf("\n Deleted element -> %d \n", element);
    return (element);
  }
}

// Display the queue
void display() {
  int i;
  if (isEmpty())
    printf(" \n Empty Queue\n");
  else {
    printf("\n Front -> %d ", front);
    printf("\n Items -> ");
    for (i = front; i != rear; i = (i + 1) % SIZE) {
      printf("%d ", items[i]);
    }
    printf("%d ", items[i]);
    printf("\n Rear -> %d \n", rear);
  }
}
2.3 比较两种方法的实现结果

    编写main.c,内容如下。因为首先验证新增变量的方法,所以注释掉文件头部的queue.define.c文件。

#include "queue.addvar.c"
//#include "queue.define.c"

int main() {
  // Fails
  deQueue();

  enQueue(1);
  enQueue(2);
  enQueue(3);
  enQueue(4);
  enQueue(5);
  enQueue(6);
  enQueue(7);
  enQueue(8);

  // Fails
  enQueue(9);

  display();
  deQueue();

  display();

  enQueue(10);
  display();

  // Fails
  enQueue(11);

  return 0;
}

    编译执行,结果如下:


 Queue is empty !! 

 Inserted -> 1
 Inserted -> 2
 Inserted -> 3
 Inserted -> 4
 Inserted -> 5
 Inserted -> 6
 Inserted -> 7
 Inserted -> 8
 Queue is full!! 

 Front -> 0 
 Items -> 1 2 3 4 5 6 7 8 
 Rear -> 7 

 Deleted element -> 1 

 Front -> 1 
 Items -> 2 3 4 5 6 7 8 
 Rear -> 7 

 Inserted -> 10
 Front -> 1 
 Items -> 2 3 4 5 6 7 8 10 
 Rear -> 0 

 Queue is full!!

    如法炮制,注释掉main.c中的queue.addvar.c,验证重新定义队列空与满的方法,编译执行,结果如下:


 Queue is empty !! 

 Inserted -> 1
 Inserted -> 2
 Inserted -> 3
 Inserted -> 4
 Inserted -> 5
 Inserted -> 6
 Inserted -> 7
 Queue is full!! 

 Queue is full!! 

 Front -> 1 
 Items -> 1 2 3 4 5 6 7 
 Rear -> 7 

 Deleted element -> 1 

 Front -> 2 
 Items -> 2 3 4 5 6 7 
 Rear -> 7 

 Inserted -> 10
 Front -> 2 
 Items -> 2 3 4 5 6 7 10 
 Rear -> 0 

 Queue is full!!

    很明显,可以看到该方法实际使用的内存空间比分配的内存空间少1。有了接口函数,不用操心具体的实现过程了,真的很爽。

参考文献

[1]Kenneth A.Reek.C和指针[M].人民邮电出版社:北京,2009:364-369.
[2]严蔚敏,吴伟民.数据结构(第二版)[M].清华大学出版社:北京,2010:63-64.
[3]Parewa Labs Pvt. Ltd.Circular Queue[EB/OL].https://www.programiz.com/dsa/circular-queue,2020-01-01.
[4]Arpit Gaurav, AshwinGoel.Circular Queue | Set 1 (Introduction and Array Implementation)[EB/OL].https://www.geeksforgeeks.org/circular-queue-set-1-introduction-array-implementation/,2020-01-01.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值