在设计循环队列

时间:2019-04-13 11:03:11

标签: python queue

我正在从问题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._forntself._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掩盖了大多数实现细节,所以

frontrear用作-1有什么好处?

1 个答案:

答案 0 :(得分:1)

我总结了常见解决方案和您的解决方案之间的主要区别是:

  1. 使用rear指针标记尾巴
  2. 当循环队列为空时,将frontrear设置为-1
  3. 更多if else个逻辑分支

总的来说,我认为这两种解决方案并没有太大的区别,只是趋势有所不同。如果您想知道其背后的细微差别,我将为您解释。

您认为,您倾向于使用较少的变量,并尝试将所有逻辑统一在一起,并使代码清晰易懂。

相反,他想提高性能(也许只是一点点),并做出更好的摘要。我将详细解释:

  1. 增强性能

    • 您可以将rear视为“ 时间交易空间”。与rear相关的每个地方,您都将用rear来计算当前的(self._front + self._len - 1) % self._capacity,但他只是从rear得到。他缓存rear以用于高频用途。
      缓存可以加快查询速度,但会增加维护成本(修改时)。因此,实际上是否应该使用缓存取决于场景。如果查询比修改更频繁,则应使用它。在此特定问题中,如果Rear使用了多于enQueuedeQueue,则可以使用rear来降低重新计算成本。
    • 他在ifelse中使用了更多的enQueue deQueue逻辑分支。通过处理特定条件可以稍微提高性能。具体来说,在空,满或单元素情况下,它会减少+-%操作。
  2. 抽象
    他的解决方案是面向对象的设计。对于循环队列,哪些属性很重要?当然,frontrearstate(空,满或其他)。因此,他保留rear并在空时分配-1来表示特殊状态。
    良好的抽象将有益于功能可伸缩性。例如,我们想向MyCircularQueue添加更多功能,也许rearstate在这里很有帮助。

所有这些都是我个人的观点,也许不正确,仅作参考。 :)