1、简介
普通队列的出队操作的时间复杂度为O(n)(因为要整体移动元素),而循环队列就可以完美解决这个问题,使得出队与入队操作均为O(1)的时间复杂度。
两个关键索引:
head:队首的索引tail:队尾的索引
出队维护 head,入队维护 tail。
两个关键状态:
-
空队列:
head == tail,因为head是闭区间,tail是开区间,即[head, tail),当head和tail相等时,区间内不包含任何元素,索引认定队列是空队列。初始化时空的循环队列中head和tail都为0。 -
满队列:
(tail + 1) % capacity == front,想象一下,一直在入队操作,以至于tail和front相等了。不错,这个时候确实可以代表满员了,因为tail是开区间嘛,没问题~但是前面空队列的判断条件也是front和tail相等呀,此时出现了二义性,因此在这里我们牺牲一个空间,只能让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()
循环队列(Circular Queue)的高效实现
循环队列解决了普通队列出队操作时间复杂度高的问题,其出队和入队操作均为O(1)。队首和队尾通过特定索引维护,当两者相等时表示队列为空;满队列的判断则是在索引+1对数组长度取余后等于队首索引,以避免与空队列混淆。文章介绍了循环队列的基本概念及使用t实现的方法。
711

被折叠的 条评论
为什么被折叠?



