一句话说透数据结构里面的循环队列

139 阅读2分钟

一句话总结:
循环队列就像环形停车场——车开进去绕一圈,出来时腾出位置,永远不会浪费停车位!


一、循环队列解决啥问题?

普通队列用数组实现时,前面的空间出队后无法复用,就像停车场出口堵住后,入口前的空位永远浪费。循环队列通过把数组连成环,让车(数据)可以循环利用空间。


二、核心结构图解

初始状态:  
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]
}

四、关键操作解析

  1. 入队(enqueue

    • 检查是否已满
    • 插入到 rear 位置
    • rear 环形移动:(rear + 1) % capacity
  2. 出队(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),固定大小
适用场景数据量不确定需要固定大小缓冲(如音频播放缓冲)

七、常见坑点

  1. 队满判断:不能直接用 rear == front(与队空条件冲突),需额外变量(如 size
  2. 指针移动:必须用取模运算 % 实现环形移动
  3. 元素类型:Kotlin数组需明确类型(示例用 Any,实际可替换为泛型)

口诀:
循环队列转圈圈,
进进出出省空间。
固定缓冲它最强,
音频视频都用上!