一、定义
队列 (Queue) 是一种先进先出FIFO (First in First Out) 的线性存储结构。
队列的特殊性:
1. 元素只能从队列的队尾进入,从队头出去。
2. 队列中各个元素的进出必须遵循“先进先出”的原则,即最先入队的元素必须最先出队。
二、队列类型
1. 顺序队列
顺序队列指的是用顺序表模拟实现的队列存储结构。在编程语言中一般采用数组实现顺序表。
初始化:定义长度为n的数组,定义两个变量 (front和rear) 分别记录队头和队尾的下标。初始化时front和rear都指向第0个位置。
入队:存储新元素,并将rear + 1。当front等于0,rear等于n时,表示队列已满。
出队:废弃当前第一个元素,并将front + 1。当front的值和rear相等时,表示队列已空。
缺陷:
顺序队列前面的空闲空间无法再被使用,造成空间浪费。
当顺序队列移动至顺序表尾部时,即便顺序表中有空闲空间,新元素也无法成功入队,一般称这种现象称为“假溢出”。
解决办法:可进行数据搬移,重新利用空闲空间。
但这样会增加时间复杂度,效率较低。循环队列能更有效的利用队列空间,且不需要移动数据。
2. 循环队列
所谓循环队列,本质仍是用顺序表模拟实现队列,只不过在具体实现的过程中,会将顺序表想象成首尾相连的环状表来用。
在循环队列中,末尾元素的下一个元素不是数组外,而是数组的头元素。这样就能够再次使用front之前的存储空间了。
初始化:定义长度为n的数组,定义两个变量 (front和rear) 分别记录队头和队尾的下标。初始化时front和rear都指向第0个位置。
入队:存储新元素,rear = rear + 1,考虑到rear在n - 1处时再加1会超过范围 (数组长度为n,rear的范围为0 ~ n - 1),所以需要对rear + 1 模运算,以防其超过范围。即rear = (rear + 1) % n
循环队列队满时front == rear,这和队空的条件一样,无法区分队空还是队满。为了解决该冲突,有三种方法:
① 当front和rear之间剩余一个空闲空间时,就视为队满
即rear + 1 = front则队满。考虑到rear在n - 1处时再加1会超过范围,所以需要对rear + 1 模运算,以防其超过范围。队满条件最终为 (rear + 1) % n = front
② 记录元素个数,以此来判断队满或队空。size = 0,入队size + 1,出队size - 1。或者直接取:
rear >= front:元素个数为size = rear - front
rear < front:元素个数为size = front - rear = n - (rear - front) = rear - front + n;
综合一下,元素个数为size = (rear - front + n) % n
③ 定义一个标识flag,入队flag = 1,出队flag = 0。front == rear且flag == 1时队满;front == rear且flag == 0时队空
出队:废弃当前第一个元素,并将front + 1。当front的值和rear相等时,表示队列已空。
3. 链式队列
链式队列,简称链队列,即使用链表实现的队列存储结构。链队列采用链式存储结构保存数据元素,允许添加无限多个数据元素,不会出现列满的问题。
入队:创建一个新节点,将原rear节点的next指向新节点,再将rear指向新节点
出队:将原front节点指向原front节点的next节点