数据结构——循环队列的实现(下)

简介: 数据结构——循环队列的实现(下)

数据结构——循环队列的实现(上):https://developer.aliyun.com/article/1496804

4.用数组实现循环队列

4.1设计队列结构

前面我们提到过设计队列时考虑加上一个size记录存放节点个数以便判断队列是否为空

typedef struct {
   int front;//首下标
   int rear;//尾下标
   int* a;//数组
     int k;//记录k值
} MyCircularQueue;

这里采用一个结构体封装,并记录数组的首尾下标,并保留一个k来记录k值,以便后面使用。

4.2构造队列(k+1个节点)

MyCircularQueue(k): 构造器,设置队列长度为 k

MyCircularQueue* myCircularQueueCreate(int k) {

  
  //开辟循环队列空间
  MyCircularQueue* queue = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if (queue == NULL)
  {
    perror("malloc fail1");
    return NULL;
  }
    //开辟数组空间
    queue->a = (int*)malloc(sizeof(int)*(k+1));
    if (queue->a == NULL)
  {
    perror("malloc fail2");
    return NULL;
  }
  
  queue->front = 0;
  queue->rear = 0;//尾指向最后一个元素的下一个
  queue->k = k;//记录k,后面使用
  return queue;
  
}


这里注意开辟了(k+1)个节点空间,采用的是前面说的第二种情况,rear指向最后一个元素的下一个位置。

4.3向循环队列插入一个元素

myCircularQueueEnQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。代码如下:

//插入数据
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
  assert(obj);
  //判断队列是否满了
  if(myCircularQueueIsFull(obj))
    {
        return false;
    }
  
  //插入队列
  //1.队列无元素的情况
  if (myCircularQueueIsEmpty(obj))
    {
        obj->a[obj->front] = value;
        obj->rear++;
        obj->rear %= obj->k+1;
        return true;
    }
//2.队列有元素的情况
    obj->a[obj->rear] = value;
    obj->rear ++;
     obj->rear %= obj->k+1;

    return true;
}

这里也分两种情况来插入,💥💥此外还要注意插入元素之后rear要++,但是如果rear超过数组的长度k+1就会造成越界访问,所以这里提供了一个方法:每次rear++之后再与k+1取模运算,这样就可以得到小于等于k的值,不会造成越界访问。

4.4判断循环队列是否为空

myCircularQueueIsEmpty(): 检查循环队列是否为空。

bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
  assert(obj);
  if (obj->front == obj->rear)
    return true;
  return false;
}

因为rear指向最后一个元素的下一个元素,所以当rear等于front时,数组为空,此时一个数据也没有插入。

4.5判断循环队列是否满了

myCircularQueueIsFull(): 检查循环队列是否已满。

bool myCircularQueueIsFull(MyCircularQueue* obj)
{
  assert(obj);
  if((obj->rear+1)%(obj->k+1)==obj->front)
    {
        return true;
    }
    return false;
}

数组并非像链表那样有pNext指针指向下一个节点,链表可以形成天然的循环结构,而数组却要依靠首尾下标来模拟实现,如图所示有两种情况:

当删除节点时只需将front++即可,所以front位置可能不是0;

4.6从循环队列中删除一个元素

myCircularQueueDeQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。注意队列先进先出,是头删。

//删除数据
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
  assert(obj);
  if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
  //头删,因为是先进先出
  
  obj->front++;
    obj->front %= obj->k+1;
  
    return true;
}

注意这里front也是一样,在+1后可能大于k造成越界,所以要进行取模运算。

4.7 从队首获取元素

myCircularQueueFront: 从队首获取元素。如果队列为空,返回 -1 。

int myCircularQueueFront(MyCircularQueue* obj) 
{
  assert(obj);
  if(myCircularQueueIsEmpty(obj))
        return -1;
  return obj->a[obj->front];
}

4.8获取队尾元素

Rear: 获取队尾元素。如果队列为空,返回 -1 。

int myCircularQueueRear(MyCircularQueue* obj) 
{
  assert(obj);
  if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
  return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}

这里要注意本来应该返回rear-1;因为rear指向尾节点的下一位,但是如果rear值为0时,再-1就变成-1了,也会造成越界访问,所以也要进行取模运算(rear-1+k+1)%(k+1),即可,因为数组有k+1个节点所以要+(k+1)并%(k+1)

3.9销毁循环队列

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

3.10结果如下:

5.结语

链表来实现循环队列有一个好处就是形成了天然的环形结构,对应数组实现循环队列则需要front,rear不断进行取模运算以防越界;但是链表实现需要手动将开辟的节点链接在一起,数组则不一样它一开辟就是地址连续的一段空间;其他的实现链表和数组都差不多;

以上就是循环队列的实现啦~ 大家有什么疑问可以写在评论区或者私信我哦~ 完结撒花~🥳🥳🎉🎉🎉

相关文章
|
6月前
|
存储 Java 容器
深入浅出 栈和队列(附加循环队列、双端队列)
深入浅出 栈和队列(附加循环队列、双端队列)
|
6月前
|
存储
【数据结构】循环队列
【数据结构】循环队列
60 0
|
6月前
|
算法 调度 C++
【C/C++ 数据结构 线性表】C/C++中队列的原理与实现:从基础到循环队列
【C/C++ 数据结构 线性表】C/C++中队列的原理与实现:从基础到循环队列
153 0
数据结构第九弹---循环队列
数据结构第九弹---循环队列
|
算法 C语言
【数据结构】循环队列
文章目录 📑前言 🍁一、循环队列的结构 💭 二、循环队列的操作 1.定义循环队列 2.创建循环队列 3.判断满
|
存储 算法 编译器
【霍洛维兹数据结构】栈和队列 | 动态循环队列 | 迷宫问题 | 表达式 | 多重栈&多重队列
【霍洛维兹数据结构】栈和队列 | 动态循环队列 | 迷宫问题 | 表达式 | 多重栈&多重队列
86 0
|
5月前
|
存储 算法
【数据结构和算法】--队列的特殊结构-循环队列
【数据结构和算法】--队列的特殊结构-循环队列
32 0
|
1月前
|
存储
【初阶数据结构】深入解析循环队列:探索底层逻辑
【初阶数据结构】深入解析循环队列:探索底层逻辑
|
4月前
|
存储 索引
【数据结构OJ题】设计循环队列
力扣题目——设计循环队列
34 1
【数据结构OJ题】设计循环队列
|
3月前
|
算法
【数据结构与算法】循环队列
【数据结构与算法】循环队列
23 0