【问题标题】:Initialize queue.front as -1 or 0 in Designing Circular Queue在设计循环队列中将 queue.front 初始化为 -1 或 0
【发布时间】:2019-04-13 11:03:11
【问题描述】:

我正在从问题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 有什么好处?

【问题讨论】:

    标签: python queue


    【解决方案1】:

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

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

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

    在您看来,您倾向于使用较少的变量,并尝试将所有逻辑统一在一起,并使代码简洁易懂。

    相比之下,他想提高性能(也许只是一点点),并做出更好的抽象。我会详细解释:

    1. 提升性能

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

    所有这些都是我的个人意见,可能不正确,仅供参考。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-14
      • 2011-01-09
      • 2013-11-10
      • 2020-12-25
      • 1970-01-01
      • 1970-01-01
      • 2014-05-12
      • 2012-06-24
      相关资源
      最近更新 更多