Инициализируйте queue.front как -1 или 0 в Проектировании Круговой очереди - PullRequest
1 голос
/ 13 апреля 2019

Я изучаю очередь из задачи Циркулярная очередь проектирования - LeetCode

Дизайн вашей реализации круговой очереди. Круговая очередь - это линейная структура данных, в которой операции выполняются на основе принципа FIFO (First In First Out), и последняя позиция соединяется обратно с первой позицией для образования круга. Его также называют «кольцевой буфер».

Одним из преимуществ кольцевой очереди является то, что мы можем использовать пробелы перед очередью. В обычной очереди, когда очередь заполнится, мы не сможем вставить следующий элемент, даже если перед ней есть место. Но используя круговую очередь, мы можем использовать пространство для хранения новых значений.

Ваша реализация должна поддерживать следующие операции:

  • 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].
  • Пожалуйста, не используйте встроенную библиотеку очередей.

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

1 Ответ

1 голос
/ 13 апреля 2019

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

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

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...