基础数据结构 2.4 队列及其变种(循环队列、优先队列)

1. 队列概述

队列(Queue)是一种先进先出(FIFO, First In First Out)的数据结构。它的基本操作包括入队(enqueue)和出队(dequeue)。在队列中,元素的插入总是在队尾进行,而元素的删除总是在队头进行。队列广泛应用于计算机科学中,例如在任务调度、数据缓冲、广度优先搜索等场景中。

1.1 队列的基本操作

  • 入队(Enqueue):将一个元素添加到队列的尾部。
  • 出队(Dequeue):从队列的头部移除一个元素并返回该元素。
  • 查看队头元素(Front/Peek):返回队头元素但不移除它。
  • 检查队列是否为空(IsEmpty):判断队列是否为空。
  • 获取队列的大小(Size):返回队列中元素的数量。

1.2 队列的实现

队列可以通过数组或链表来实现。下面分别展示这两种实现方式。

1.2.1 数组实现队列

class ArrayQueue:
    def __init__(self, capacity):
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0
        self.rear = 0
        self.size = 0

    def is_empty(self):
        return self.size == 0

    def is_full(self):
        return self.size == self.capacity

    def enqueue(self, item):
        if self.is_full():
            raise Exception("Queue is full")
        self.queue[self.rear] = item
        self.rear = (self.rear + 1) % self.capacity
        self.size += 1

    def dequeue(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        item = self.queue[self.front]
        self.queue[self.front] = None  # Optional: Clear the reference
        self.front = (self.front + 1) % self.capacity
        self.size -= 1
        return item

    def peek(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        return self.queue[self.front]

    def get_size(self):
        return self.size

1.2.2 链表实现队列

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

class LinkedListQueue:
    def __init__(self):
        self.front = None
        self.rear = None
        self.size = 0

    def is_empty(self):
        return self.size == 0

    def enqueue(self, item):
        new_node = Node(item)
        if self.is_empty():
            self.front = self.rear = new_node
        else:
            self.rear.next = new_node
            self.rear = new_node
        self.size += 1

    def dequeue(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        item = self.front.value
        self.front = self.front.next
        if self.front is None:  # If the queue is now empty
            self.rear = None
        self.size -= 1
        return item

    def peek(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        return self.front.value

    def get_size(self):
        return self.size

1.3 队列的优缺点

优点

  • 简单易用:队列的操作简单,易于理解和实现。
  • 高效:在大多数情况下,队列的入队和出队操作时间复杂度为 O(1)。

缺点

  • 固定大小:使用数组实现的队列在容量上是固定的,可能导致空间浪费或溢出。
  • 内存开销:链表实现的队列在每个节点上都有额外的内存开销。

2. 循环队列

循环队列(Circular Queue)是对线性队列的一种改进,解决了线性队列在出队后可能造成的空间浪费问题。循环队列通过将队列的尾部与头部连接起来,使得队列的使用更加高效。

2.1 循环队列的实现

循环队列的实现与线性队列类似,但在入队和出队时需要使用模运算来处理索引的循环。

class CircularQueue:
    def __init__(self, capacity):
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0
        self.rear = 0
        self.size = 0

    def is_empty(self):
        return self.size == 0

    def is_full(self):
        return self.size == self.capacity

    def enqueue(self, item):
        if self.is_full():
            raise Exception("Queue is full")
        self.queue[self.rear] = item
        self.rear = (self.rear + 1) % self.capacity
        self.size += 1

    def dequeue(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        item = self.queue[self.front]
        self.queue[self.front] = None  # Optional: Clear the reference
        self.front = (self.front + 1) % self.capacity
        self.size -= 1
        return item

    def peek(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        return self.queue[self.front]

    def get_size(self):
        return self.size

2.2 循环队列的优缺点

优点

  • 空间利用率高:循环队列有效利用了数组的空间,避免了线性队列中的空间浪费。
  • 操作简单:与线性队列相比,循环队列的操作逻辑相似,易于实现。

缺点

  • 固定大小:与线性队列一样,循环队列的大小在创建时固定,可能导致空间不足。
  • 复杂性增加:实现时需要处理索引的循环,增加了实现的复杂性。

3. 优先队列

优先队列(Priority Queue)是一种特殊的队列,其中每个元素都有一个优先级。出队时,优先级高的元素会优先被移除。优先队列通常使用堆(Heap)来实现。

3.1 优先队列的实现

优先队列可以使用最小堆或最大堆来实现。以下是一个使用最小堆实现的优先队列示例。

import heapq

class PriorityQueue:
    def __init__(self):
        self.heap = []

    def is_empty(self):
        return len(self.heap) == 0

    def enqueue(self, item, priority):
        heapq.heappush(self.heap, (priority, item))

    def dequeue(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        return heapq.heappop(self.heap)[1]

    def peek(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        return self.heap[0][1]

    def get_size(self):
        return len(self.heap)

3.2 优先队列的优缺点

优点

  • 灵活性:优先队列允许根据优先级处理元素,适用于许多应用场景,如任务调度、图算法等。
  • 高效性:使用堆实现的优先队列,入队和出队操作的时间复杂度为 O(log n)。

缺点

  • 实现复杂:优先队列的实现相对复杂,尤其是在处理优先级时。
  • 内存开销:使用堆结构可能会导致额外的内存开销。

4. 总结

队列及其变种(循环队列、优先队列)是计算机科学中非常重要的数据结构。它们各自有不同的实现方式和应用场景。选择合适的队列实现方式可以提高程序的效率和可读性。在实际应用中,开发者需要根据具体需求来选择合适的队列类型,并注意其优缺点。希望本教程能帮助你更深入地理解队列及其变种。