我正在从问题Design Circular Queue - LeetCode中学习队列
设计循环队列的实现。循环队列是一种线性数据结构,其中的操作是基于FIFO(先进先出)原理执行的,最后一个位置又连接回第一个位置以构成一个圆。也称为“环形缓冲区”。
循环队列的好处之一是我们可以利用队列前面的空间。在普通队列中,一旦队列已满,即使队列前面有空格,我们也无法插入下一个元素。但是使用循环队列,我们可以使用空间来存储新值。
您的实现应支持以下操作:
MyCircularQueue(k)
:构造函数,将队列的大小设置为k。Front
:从队列中获取最前面的项目。如果队列为空,则返回-1。Rear
:从队列中获取最后一个项目。如果队列为空,则返回-1。enQueue(value)
:将元素插入循环队列。如果操作成功,则返回true。deQueue()
:从循环队列中删除一个元素。如果操作成功,则返回true。isEmpty()
:检查循环队列是否为空。isFull()
:检查循环队列是否已满。示例:
MyCircularQueue circularQueue = new MyCircularQueue(3); // set the size to be 3 circularQueue.enQueue(1); // return true circularQueue.enQueue(2); // return true circularQueue.enQueue(3); // return true circularQueue.enQueue(4); // return false, the queue is full circularQueue.Rear(); // return 3 circularQueue.isFull(); // return true circularQueue.deQueue(); // return true circularQueue.enQueue(4); // return true circularQueue.Rear(); // return 4
注意:
- 所有值都将在[0,1000]范围内。
- 操作数将在[1,1000]范围内。
- 请不要使用内置的队列库。
我模仿古德里奇的教科书Data Structures and Algorithms in Python,并写了一个友好的可读解决方案
1,仅三个数据(_queue,_len和_front)
2,将self._front
初始化为0
class MyCircularQueue:
#Runtime: 76 ms, faster than 53.17%
#Memory Usage: 13.6 MB, less than 7.24%
def __init__(self, k: int):
"""
Initialize your data structure here. Set the size of the queue to be k.
"""
self._queue = [None] * k #the basic
self._capacity = k
self._len = 0
#The first three categorize as a group, the 4th as the second group
self._front = 0
#self._rear is not necessary, because rear = front + length -1
def enQueue(self, value: int) -> bool:
"""
Insert an element into the circular queue. Return true if the operation is successful.
"""
if self.isFull(): return False
avail = (self._front + self._len) % (self._capacity)
self._queue[avail] = value
self._len += 1
return True
def deQueue(self) -> bool:
"""
Delete an element from the circular queue. Return true if the operation is successful.
"""
if self.isEmpty():
return False
self._queue[self._front] = None #overrode the current node
self._front = (self._front+1) % self._capacity
self._len -= 1
return True
def Front(self) -> int:
"""
Get the front item from the queue.
"""
if not self.isEmpty():
return self._queue[self._front]
else:
return -1
def Rear(self) -> int:
"""
Get the last item from the queue.
"""
if not self.isEmpty():
_rear = (self._front + self._len - 1) % self._capacity
return self._queue[_rear]
else:
return -1
def isEmpty(self) -> bool:
"""
Checks whether the circular queue is empty or not.
"""
return self._len == 0
def isFull(self) -> bool:
"""
Checks whether the circular queue is full or not.
"""
return self._len == self._capacity
Goodrich的设计非常易于阅读。
但是,在阅读其他提交内容时,通常的做法是将self._fornt
和self._rear
初始化为-1
,以为正在努力阅读或写作。
摘录了一个性能优于98%的案例
def deQueue(self):
"""
Delete an element from the circular queue. Return true if the operation is successful.
:rtype: bool
"""
if self.isEmpty():
return False
self.head = (self.head+1) % self.maxlen
self.currlen -= 1
if self.isEmpty(): #have to take care of self.head and self.tail
self.head = -1
self.tail = -1
#another submission which initialize front and rear =-1
def enQueue(self, value: 'int') -> 'bool':
"""
Insert an element into the circular queue. Return true if the operation is successful.
"""
if (self.len == self.capacity): return False
# To check if full
#if (self.rear == self.front - 1 or (self.rear == self.capacity - 1 and self.front == 0) )
if (self.front == -1):
self.front, self.rear = 0, 0
elif (self.rear == self.capacity - 1 and self.front != 0):
# make rear start (case when element was removed from start)
self.rear = 0
else:
self.rear = (self.rear + 1) % self.capacity
self.data[self.rear] = value
self.len += 1
return True
由于python掩盖了大多数实现细节,所以
将front
或rear
用作-1
有什么好处?
答案 0 :(得分:1)
我总结了常见解决方案和您的解决方案之间的主要区别是:
rear
指针标记尾巴front
和rear
设置为-1
if
else
个逻辑分支总的来说,我认为这两种解决方案并没有太大的区别,只是趋势有所不同。如果您想知道其背后的细微差别,我将为您解释。
您认为,您倾向于使用较少的变量,并尝试将所有逻辑统一在一起,并使代码清晰易懂。
相反,他想提高性能(也许只是一点点),并做出更好的摘要。我将详细解释:
增强性能:
rear
视为“ 时间交易空间”。与rear
相关的每个地方,您都将用rear
来计算当前的(self._front + self._len - 1) % self._capacity
,但他只是从rear
得到。他缓存rear
以用于高频用途。Rear
使用了多于enQueue
和deQueue
,则可以使用rear
来降低重新计算成本。if
和else
中使用了更多的enQueue
deQueue
逻辑分支。通过处理特定条件可以稍微提高性能。具体来说,在空,满或单元素情况下,它会减少+-%
操作。 抽象:
他的解决方案是面向对象的设计。对于循环队列,哪些属性很重要?当然,front
,rear
和state
(空,满或其他)。因此,他保留rear
并在空时分配-1
来表示特殊状态。
良好的抽象将有益于功能可伸缩性。例如,我们想向MyCircularQueue
添加更多功能,也许rear
和state
在这里很有帮助。
所有这些都是我个人的观点,也许不正确,仅作参考。 :)