题目描述
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
分析
-
插入新元素位置是队尾后面。如果队尾已经位于list尾部,那么新元素插入到list[0]。新元素成功插入后,新元素的位置为队尾。
-
去除旧元素的位置是队首位置。旧元素成功去除后,旧元素的下一个元素位置为队首。如果旧元素是队尾的元素,队首则为list[0]的位置。
-
结合以上两点不难看出,这是一个循环的设计,它让队列变成了一个圆环。
-
但是当空列表时,队首head,队尾tail应当指向哪里?他们不应该指向任何队列内的位置———这里有两个思路:
- 设计一个n+1容量的列表,允许存放n个元素,但是空一个位置用来存放空列表时的head、tail
- 设计一个n容量的列表,当空列表时,head、tail的值为None
为了更贴近抽象概念,我在这里选择了第二种思路。
解法
动态数组法
前言
简单直接的实现思路,就是使用list的append方法,但是这已经违背了定长序列的原意——尽管这个办法简单有效。
概要
- 类的实例在初始化时应当具有的属性
- 空列表
代码
以下代码实测可用
#--coding:utf-8--
# Author: Allen Lv
# Version: python 3.8
class MyCircularQueue:
def __init__(self, k: int):
self.queue = []
def enQueue(self, value: int) -> bool:
if self.isFull():
return False
self.queue.append(value)
return True
def deQueue(self) -> bool:
if self.isEmpty():
return False
del self.queue[0]
return True
def Front(self) -> int:
if self.isEmpty():
return -1
return self.queue[0]
def Rear(self) -> int:
if self.isEmpty():
return -1
return self.queue[-1]
def isEmpty(self) -> bool:
if len(self.queue) == 0:
return True
return False
def isFull(self) -> bool:
if len(self.queue) == self.size:
return True
return False
缺点
- 违背了循环队列的愿意
优点
- 使用append实现简单(虽然python底层的list也是定容的、只是当append时进行扩容罢了)
数组法
前言
为了更接近定长数组这个概念(比如 C# 中的Arrary),我更推荐这种思路。
这种思路是:生成实例时就产生定长的空队列,根据需要往空的位置填入元素。但是无法声明一个有指定长度但是元素都是空的列表,所以我用None来代替空元素。
概要
- 类的实例在初始化时应当具有的属性:
- 长度/ 大小/ 容量
- 指定长度、且长度固定的列表
- 都为None的head、tail
- isEmpty()和isFull()方法检测的是每个元素是否为None,而非列表长度。
- Front()和Rear()给出的元素是head、tail所指向的元素,而非列表最前、列表最后的元素(把列表想象成环形的,就没有前后之分了)。
- enQueue(value)插入新元素时,如果是空列表,对head、tail都赋0。
- deQueue()删除旧元素时,如果元素是唯一的元素,删除后,对head
、tail都赋None
代码
以下代码实测可用
#--coding:utf-8--
# Author: Allen Lv
# Version: python 3.8
class MyCircularQueue:
def __init__(self, k: int):
self.queue = [None] * k
self.head = None # 开始标记
self.tail = None # 结束标记
self.size = k # 大小
# 自己测试时方便检查队列
def __str__(self):
return str(self.queue)
def isFull(self):
# 判断队列是否为满
full = True
for x in self.queue:
if x == None:
full = False
break
return full
def isEmpty(self):
# 判断队列是否为空
empty = True
for x in self.queue:
if x != None:
empty = False
break
return empty
def Front(self):
# 获取开始标记
if self.isEmpty():
return -1
return self.queue[self.head]
def Rear(self):
# 获取结束标记
if self.isEmpty():
return -1
return self.queue[self.tail]
def enQueue(self, value: int):
# 入队
if self.isFull():
return False
if self.isEmpty():
self.head = 0
self.tail = 0
self.queue[0] = value
else:
# 如果不是队尾的元素,tail += 1
if self.tail < self.size - 1:
self.tail += 1
self.queue[self.tail] = value
# 如果是队尾的元素,新元素插到队首
else:
self.tail = 0
self.queue[0] = value
return True
def deQueue(self):
# 出队
if self.isEmpty():
return False
self.queue[self.head] = None
# 出队一个以后,判断后面还有没有元素
# 如果没有元素了
if self.isEmpty():
self.head = None
# 如果还有元素
else:
# 如果不是队尾的元素,head += 1
if self.head < self.size - 1:
self.head += 1
# 如果是队尾元素,队首元素记为结束标记
else:
self.head = 0
return True
缺点
- 过多的判断语句
- 过多的循环语句
优点
- 和概念十分接近,易于理解
测试方法
如何进行本地测试?
这是Leetcode给出的测试指令集,我需要一个方法来使用它,然后在本地对代码进行测试(本地测试更容易发现bug和debug)
["MyCircularQueue","enQueue","enQueue","enQueue","enQueue","Rear","isFull","deQueue","enQueue","Rear"]
[[3],[1],[2],[3],[4],[],[],[],[4],[]]
比如我解法2的py文件叫CircularQueue2.py,那么我们可以在同目录下创建测试文件,test.py。
代码如下:
#--coding:utf-8--
# Author: Allen Lv
# Version: python 3.8
from CircularQueue2 import *
def doTest(action: list, param: list):
n = len(action)
if n != len(param):
raise IndexError
p = MyCircularQueue(3)
for i in range(1, n):
code = "print(p.{}({}))".format(action[i], pa[0] if (pa := param[i]) != [] else "")
try:
exec(code)
except:
print(code)
a = input("-------stop------")
if __name__ == "__main__":
action = ["MyCircularQueue","enQueue","enQueue",
"enQueue","enQueue","Rear","isFull",
"deQueue","enQueue", "__str__","Rear"]
param = [[3],[1],[2],[3],[4],[],[],[],[4],[],[]]
doTest(action, param)
# p = MyCircularQueue(3)
# p.enQueue(2)
# print(p.__str__())
doTest函数使用exec()对被str.fomat格式化的代码进行测试
写在最后
我是Allen Lv,一名python初学者,如果你对本文有任何改进意见,欢迎提出,非常感谢。