C语言小知识——循环队列

文章目录

  • 前言
  • 一、循环队列的直观印象
  • 二、“双指针+数组”实现循环队列
    • 1.初始化
    • 2.入队
    • 3.出队
    • 4.队列长度
  • 三、队空与队满的判断
    • 1.队空判断
    • 2.队满判断
  • 总结

前言

在算法中,队列是个常用的数据类型,具有先进先出的存储特点。

在python中我们直接调用 collections.deque() 就可以构建一个队列,那么在C语言中,该如何简单地实现一个队列呢?


一、循环队列的直观印象

为了节省内存空间,C中的队列要用循环队列实现,它可以循环使用位置存放数据。以此实现新入队的数据,直接覆盖前面已出队的无用数据。 而不需要考虑如何删除已出队的元素,并且使得队列的长度可以是有限的,只要满足同时要存放的最多元素个数即可。
循环队列可视化的图如下:

C语言小知识——循环队列_第1张图片

先不管这个圈,我们把它拉直的话就是一个数组,下标从0开始。它有一个最大长度的设置,记为 M a x s i z e Maxsize Maxsize,在初始化时要定义好。在上图中, M a x s i z e = 8 Maxsize=8 Maxsize=8

但要注意,在我们使用时,这个队列能同时存放的最大元素个数是 M a x s i z e − 1 = 7 Maxsize-1=7 Maxsize1=7。这是为了能有效判断队满还是队空,在下面会讲到。

除了数组,我们还设有两个指针:队头指针(front)用于指示队首元素的位置,队尾指针(rear)用于指示队列最后一个元素的位置。

  • 初始情况下,两个指针均在数组的 i n d e x = 0 index=0 index=0 位置处;
  • 入队时,在队尾插入,rear++;
  • 出队时,从队头取出,front++。

二、“双指针+数组”实现循环队列

1.初始化

初始化过程中,就是定义一个数组,和两个指针。当然这个指针不真就是C语言中的指针 ∗ * 数据类型,而是说可以代表数组中元素位置的“东西”。那么最简单的办法,front 和 rear 直接是数组下标最好。

我们初始化一个存放元素是整数的队列 d e q u e deque deque,并且希望它最多能同时存放 m a x L e n maxLen maxLen个数据:

int deque[maxLen+1];  //注意初始化时,数组长度要在数据长度基础上加1
int front=0, rear=0;

注意数组长度是 m a x L e n + 1 maxLen+1 maxLen+1

2.入队

当想将一个元素加入队列时,我们知道要加在队尾,此时队尾指针rear指向的数组位置就是可以添加新元素的位置。因此直接将新元素赋给rear指向的位置,再将rear后移一位。

为了防止rear的值超出数组长度,我们在后移rear后,对数组长度取个余,让rear循环地指回数组前面的位置。不过,如果我们能很好地设定数组的长度,保证能一次性放下所有元素,这一步就不是必要的,

int in=5; //要入队的元素
deque[rear] = in;
rear ++;
// 或者:
// deque[rear++] = in;
rear = rear%(maxLen+1);

3.出队

出队时从队首弹出第一个元素,front指针指向的恰好就是队列的第一个元素。因此直接获取front指向的元素,并将front后移一位。后移front是因为当前值在主观上已经被弹出,虽然它客观上还在这个数组中,但已经可以看作不存在于数组中了;后面的一个值才是真正的队首元素,需要将front指向它。

同样,对后移后的front取余。

突然感觉有点哲学意味,啧…

int out; //表示弹出的元素
out = deque[front];
front ++;
// 或者:
// out = deque[front++];
front = front%(maxLen+1);

4.队列长度

可以看出rear指针指向队尾,但它指向的位置永远是还没有存储新元素的(因为我们插入新元素后,马上将rear后移了);front指向队首元素,指向的位置是有元素的。
所以,队列中有效元素的个数(即队列长度)是

  • r e a r − f r o n t rear-front rearfront ( r e a r > = f r o n t rear>=front rear>=front)
  • r e a r − f r o n t + m a x L e n + 1 rear-front+maxLen+1 rearfront+maxLen+1 ( r e a r < f r o n t rearrear<front)

而不是 r e a r − f r o n t + 1 rear-front+1 rearfront+1

三、队空与队满的判断

1.队空判断

直观理解,当队列长度为0时,队空。因此 r e a r = = f r o n t rear==front rear==front就是队空条件。

2.队满判断

还记得吗~在定义队列数组时,我们给数组长度刻意多加了个1,这是为了留出来一个多余的位置不放元素。这样,我们才能区分队满和队空的状态。
队满时,rear和front的位置关系是这个亚子的:
C语言小知识——循环队列_第2张图片
直观来看,转圈圈方向上,rear在front前一个位置的时候,队列就满了。从数学上严谨推导一下,
( r e a r + 1 ) % ( m a x L e n + 1 ) = = f r o n t (rear+1)\%(maxLen+1)==front (rear+1)%(maxLen+1)==front时,队满。

这时,我们思考一下为什么不能让元素把数组全占满?试想如果上图中一圈全有元素了,那么rear会指向front的那个位置,这时就会出现 f r o n t = = r e a r front==rear front==rear的情况。不觉得眼熟嘛~这就是判断队空的条件啊。

所以,如果不在数组中留一个空位置的话,队满和队空无法区分。


总结

我看很多教程都会定义一个队列的结构体,确实很规范。我把其中的基本原理和操作放在这里,用起来会比较灵活。以后要写结构体的话应该也会吧hhhhh

配套练习leetcode127. 单词接龙 。看懂就要用啊~

你可能感兴趣的:(C语言,队列,c语言)