4、循环队列(circle queue)

循环队列(Circular Queue)的高效实现
循环队列解决了普通队列出队操作时间复杂度高的问题,其出队和入队操作均为O(1)。队首和队尾通过特定索引维护,当两者相等时表示队列为空;满队列的判断则是在索引+1对数组长度取余后等于队首索引,以避免与空队列混淆。文章介绍了循环队列的基本概念及使用t实现的方法。
1、简介

普通队列的出队操作的时间复杂度为O(n)(因为要整体移动元素),而循环队列就可以完美解决这个问题,使得出队与入队操作均为O(1)的时间复杂度。

两个关键索引

  • head:队首的索引
  • tail:队尾的索引

出队维护 head,入队维护 tail

两个关键状态

  • 空队列:head == tail,因为 head 是闭区间,tail 是开区间,即[head, tail),当 headtail 相等时,区间内不包含任何元素,索引认定队列是空队列。初始化时空的循环队列中 headtail 都为 0

  • 满队列:(tail + 1) % capacity == front,想象一下,一直在入队操作,以至于tailfront相等了。不错,这个时候确实可以代表满员了,因为tail是开区间嘛,没问题~但是前面空队列的判断条件也是fronttail相等呀,此时出现了二义性,因此在这里我们牺牲一个空间,只能让tail到达front前面的一个位置,就表示满了。还有一个小坑就是要对capacity取余,想一下tail在数组的最后一个位置,索引为capacity-1,此时入队必然overflow,因此加一后对capacity取余回到索引0位置,完成循环的特性~这种小操作以后会用到很多的。

2、实现

使用 Python list 实现 circle queue

# -*- coding: utf-8 -*-
"""
    Description: 循环队列(基于 Python list)
"""


class CircularQueue(object):
    """固定长度的循环队列"""

    def __init__(self, capacity=10):
        """循环队列中,实际元素的数量的最大值 == 队列长度 - 1
        也就是说在满队列的情况下,会浪费一个存储空间
        """
        self._capacity = capacity + 1  # 循环队列的长度,即循环队列的容量
        self.c_queue = [None] * self._capacity
        self.head = 0
        self.tail = 0
        self.size = 0  # 循环队列中,实际元素的数量

    def __len__(self) -> int:
        return self.size

    def is_empty(self) -> bool:
        return self.size == 0
        # return self.head == self.tail

    def get_capacity(self):
        return self._capacity - 1

    def get_size(self):
        return self.size

    def first(self):
        return False if self.is_empty() else self.c_queue[self.head]

    def enqueue(self, data):
        """队尾插入
        在入队前后都需要判断队列是否满,如果满了,则进行扩容
        否则,如果先将队列塞满,然后出队,再进行入队操作,会报错
        """
        if (self.tail + 1) % self._capacity == self.head:
            self._resize(self.get_capacity() * 2)  # 队列满了,进行扩容

        self.c_queue[self.tail] = data
        self.tail = (self.tail + 1) % self._capacity
        self.size += 1

    def dequeue(self):
        """队首删除"""
        if self.is_empty():
            raise Exception("error.the circle queue is empty!")

        temp = self.c_queue[self.head]
        self.c_queue[self.head] = None
        self.head = (self.head + 1) % self._capacity
        self.size -= 1

        # 队列不为空,且有效元素个数为可容纳元素的四分之一时
        if self.get_size() and self.get_capacity() // self.get_size() == 4:
            # 缩容为原来的二分之一
            self._resize(self.get_capacity() // 2)
        return temp

    def _resize(self, capacity):
        """扩/缩容操作,将容量扩/缩至capacity(这里的capacity是面向用户,
        所以真正的self._capaciry应该是capacity+1,才能容纳capacity这么多元素呀)
        """
        # 建立一个新的 list,真实容量为 capacity + 1
        temp_list = [None] * (capacity + 1)

        # 准备遍历原先的 self.c_queue,转移元素,从 self.head 开始
        index = self.head
        while index != self.tail:
            if index >= self.head:
                temp_list[index - self.head] = self.c_queue[index]
            else:
                temp_list[index + self._capacity - self.head] = self.c_queue[index]
            index = (index + 1) % self._capacity
        # 更新 self.c_queue
        self.c_queue = temp_list

        self.tail = self.size  # 转移数据并不改变size
        self.head = 0

        self._capacity = capacity + 1

    def print(self):
        if self.is_empty():
            print("None")
        # print(self.c_queue)
        temp = self.head
        while temp != self.tail:
            print(self.c_queue[temp], end=" ")
            temp = (temp + 1) % self._capacity
        print()


if __name__ == '__main__':
    cq = CircularQueue(5)
    cq.print()
    cq.enqueue(1)
    cq.enqueue(2)
    cq.enqueue(3)
    cq.enqueue(4)
    cq.enqueue(5)
    cq.print()
    print("-----------------")

    cq.dequeue()
    cq.dequeue()
    cq.print()
    print("-----------------")

    cq.enqueue(7)
    cq.enqueue(6)
    cq.enqueue(10)
    cq.print()
    print("-----------------")
    #
    # cq.first()

### Java 实现循环队列 #### 基于数组的循环队列 为了实现一个高效的循环队列,可以利用数组来存储元素,并通过两个指针 `front` 和 `rear` 来追踪队首和队尾位置。当 `rear` 到达数组末尾时,将其重置为数组起始位置,从而形成环形结构。 ```java public class CircularQueue<T> { private T[] elements; private int front = 0; private int rear = -1; private final int capacity; @SuppressWarnings("unchecked") public CircularQueue(int capacity) { this.capacity = capacity; this.elements = (T[]) new Object[capacity]; } public boolean isFull() { return (rear + 1) % capacity == front; } public boolean isEmpty() { return front == (rear + 1) % capacity && size() == 0; } public void enqueue(T element) throws Exception { if (isFull()) throw new Exception("Queue Overflow"); rear = (rear + 1) % capacity; elements[rear] = element; } public T dequeue() throws Exception { if (isEmpty()) throw new Exception("Queue Underflow"); T item = elements[front]; elements[front] = null; front = (front + 1) % capacity; return item; } public int size() { if (rear >= front) { return rear - front + 1; } else { return rear + 1 + capacity - front; } } } ``` 此代码展示了如何创建固定容量的循环队列并处理入队 (`enqueue`) 和出队 (`dequeue`) 操作[^1]。 #### 基于单向链表的循环队列 另一种方法是采用单向链表作为底层数据结构。这种方式允许动态调整队列长度而不受预设容量限制。对于基于链表的循环队列而言,在每次插入新节点之后都将最后一个节点链接回第一个节点以保持闭环特性。 ```java class Node<E> { E data; Node<E> next; Node(E data) { this.data = data; this.next = null; } } public class LinkedListCircularQueue<E> { private Node<E> head; private Node<E> tail; private int count; public LinkedListCircularQueue() { head = null; tail = null; count = 0; } public boolean isEmpty() { return count == 0; } public void enqueue(E value) { Node<E> newNode = new Node<>(value); if (tail == null) { head = newNode; tail = newNode; newNode.next = head; // Make it circular. } else { tail.next = newNode; tail = newNode; tail.next = head; // Keep the circle closed after adding a node. } ++count; } public E dequeue() throws Exception { if (isEmpty()) throw new Exception("Cannot remove from an empty queue"); E result = head.data; if (head == tail) { // If there's only one element in the queue, head = null; // then set both pointers to null and break the loop. tail = null; } else { head = head.next; tail.next = head; // Update last pointer so that it points back at the first. } --count; return result; } public int getSize() { return count; } } ``` 上述实现了使用单向链表构建的循环队列类,其中包含了基本的操作如加入(`enqueue`)、移除(`dequeue`)以及查询当前大小(`getSize`)[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值