一句话总结:
循环队列就像环形停车场——车开进去绕一圈,出来时腾出位置,永远不会浪费停车位!
一、循环队列解决啥问题?
普通队列用数组实现时,前面的空间出队后无法复用,就像停车场出口堵住后,入口前的空位永远浪费。循环队列通过把数组连成环,让车(数据)可以循环利用空间。
二、核心结构图解
初始状态:
front = 0, rear = 0
[空, 空, 空, 空]
入队A、B:
front=0, rear=2
[A, B, 空, 空]
出队A后:
front=1, rear=2
[空, B, 空, 空]
再入队C、D:
front=1, rear=0
[D, B, C, 空] (rear绕回起点)
三、Kotlin 代码实现
class CircularQueue(private val capacity: Int) {
private val elements = arrayOfNulls<Any>(capacity)
private var front = 0 // 队头指针
private var rear = 0 // 队尾指针(下一个要插入的位置)
private var size = 0 // 当前元素数量
// 入队(停车)
fun enqueue(item: Any): Boolean {
if (isFull()) return false // 停车场满了,进不去
elements[rear] = item
rear = (rear + 1) % capacity // 环形移动
size++
return true
}
// 出队(开走)
fun dequeue(): Any? {
if (isEmpty()) return null // 停车场空的,没车可出
val item = elements[front]
front = (front + 1) % capacity
size--
return item
}
// 是否满员(车位已占满?)
fun isFull(): Boolean = size == capacity
// 是否为空(停车场没车?)
fun isEmpty(): Boolean = size == 0
// 看一眼队头(最老的车是谁?)
fun peek(): Any? = if (isEmpty()) null else elements[front]
}
四、关键操作解析
-
入队(
enqueue)- 检查是否已满
- 插入到
rear位置 rear环形移动:(rear + 1) % capacity
-
出队(
dequeue)- 检查是否为空
- 取出
front位置的元素 front环形移动:(front + 1) % capacity
五、测试用例
fun main() {
val parkingLot = CircularQueue(3) // 建一个3车位的环形停车场
println(parkingLot.enqueue("Audi")) // true
println(parkingLot.enqueue("BMW")) // true
println(parkingLot.peek()) // Audi
println(parkingLot.dequeue()) // Audi
println(parkingLot.enqueue("Tesla")) // true
println(parkingLot.enqueue("BYD")) // true(此时已满)
println(parkingLot.enqueue("NIO")) // false(车位已满,进不去)
println(parkingLot.dequeue()) // BMW
println(parkingLot.enqueue("NIO")) // true(腾出空位,可以进)
}
六、与普通队列对比
| 特性 | 普通队列 | 循环队列 |
|---|---|---|
| 空间利用率 | 出队后前面空间浪费 | 全部空间可循环使用 |
| 入队/出队复杂度 | O(1)但可能触发扩容 | O(1),固定大小 |
| 适用场景 | 数据量不确定 | 需要固定大小缓冲(如音频播放缓冲) |
七、常见坑点
- 队满判断:不能直接用
rear == front(与队空条件冲突),需额外变量(如size) - 指针移动:必须用取模运算
%实现环形移动 - 元素类型:Kotlin数组需明确类型(示例用
Any,实际可替换为泛型)
口诀:
循环队列转圈圈,
进进出出省空间。
固定缓冲它最强,
音频视频都用上!