Инициализируйте queue.front как -1 или 0 в Designing Circular Queue

Я изучаю Очередь из задачи Дизайн круговой очереди - LeetCode

Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and the last position is connected back to the first position to make a circle. It is also called "Ring Buffer".

One of the benefits of the circular queue is that we can make use of the spaces in front of the queue. In a normal queue, once the queue becomes full, we cannot insert the next element even if there is a space in front of the queue. But using the circular queue, we can use the space to store new values.

Your implementation should support following operations:

  • MyCircularQueue(k): Constructor, set the size of the queue to be k.
  • Front: Get the front item from the queue. If the queue is empty, return -1.
  • Rear: Get the last item from the queue. If the queue is empty, return -1.
  • enQueue(value): Insert an element into the circular queue. Return true if the operation is successful.
  • deQueue(): Delete an element from the circular queue. Return true if the operation is successful.
  • isEmpty(): Checks whether the circular queue is empty or not.
  • isFull(): Checks whether the circular queue is full or not.

Example:

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

Note:

  • All values will be in the range of [0, 1000].
  • The number of operations will be in the range of [1, 1000].
  • Please do not use the built-in Queue library.

Я подражаю учебнику Гудрича Структуры данных и алгоритмы в 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 

Дизайн Гудрича очень удобен для чтения с меньшими усилиями.

Тем не менее, при чтении других представленных материалов общепринятой практикой является инициализация 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?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
1 030
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я резюмировал основные различия между общим решением и вашим решением:

  1. используйте указатель rear, чтобы отметить хвост
  2. когда круговая очередь пуста, установите front и rear на -1
  3. еще ifelse логические ветки

В общем, думаю, эти два решения мало чем отличаются, просто имеют разную направленность. Если вы хотите узнать небольшую разницу за этим, я объясню это для вас.

По вашему мнению, вы склонны использовать меньше переменных и пытаться унифицировать всю логику вместе, а также сделать код чистым и понятным.

Напротив, он хочет улучшить производительность (может быть, совсем немного) и сделать более качественную абстракцию. Я объясню это подробно:

  1. Повышение производительности:

    • Вы можете рассматривать rear как «обмен места на время». В каждом месте, относящемся к rear, вы будете пересчитывать текущий rear по (self._front + self._len - 1) % self._capacity, а он просто берется из rear. Он кеширует rear для высокочастотного использования.
      Кэш может ускорить запрос, но удорожает обслуживание (при изменении). Так что на самом деле, следует ли использовать кеш, зависит от сценария. Если запрос чаще, чем изменение, вы должны использовать его. В этой конкретной задаче, если Rear используется больше, чем enQueue и deQueue, вы можете использовать rear, чтобы уменьшить стоимость повторного расчета.
    • Он использовал больше ifelse логических ветвей в enQueue и deQueue. Это может немного повысить производительность в зависимости от конкретных условий. В частности, это уменьшает +-% работу при пустом, полном или одноэлементном корпусе.
  2. Абстракция:
    Его решение — более объектно-ориентированный дизайн. какие свойства важны для циклической очереди? Конечно, front, rear и state(пустой, полный или еще что). Поэтому он сохраняет rear и назначает -1, когда он пуст, чтобы представлять особое состояние.
    Хорошая абстракция пойдет на пользу функциональной масштабируемости. Например, мы хотим добавить больше функций к MyCircularQueue, возможно, rear и state помогут здесь.

Все это мое личное мнение, может и не верное, просто для информации. :)

Другие вопросы по теме