Очередь

Очередь - динамическая структура данных, основанная на принципе FIFO (First In - First Out) (Первым пришёл — первым ушёл). В очереди элементы добавляются в конец и извлекаются из начала.

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

Основные операции, которые можно выполнять с очередью, включают добавление элемента в конец очереди (enqueue), извлечение элемента из начала очереди (dequeue) и получение элемента из начала очереди без его удаления (front).

Преимущества очереди:

  1. Порядок элементов: Очередь сохраняет порядок элементов в соответствии с их добавлением. Это означает, что элементы, добавленные раньше, будут извлекаться раньше, что может быть полезно во многих сценариях.

  2. Простота использования: Очередь имеет простой интерфейс, состоящий из основных операций добавления и удаления элементов. Это делает ее легкой в использовании и понимании.

  3. Эффективность при работе с большими объемами данных: Очередь может быть эффективной при обработке больших объемов данных, так как добавление и удаление элементов происходит за .

Недостатки очереди:

  1. Ограниченный доступ: В очереди доступны только операции добавления в конец (enqueue) и удаления из начала (dequeue). Это означает, что доступ к элементам в середине очереди или изменение уже добавленных элементов может быть затруднено.

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

  3. Неэффективность при поиске: Очередь не предоставляет эффективные операции поиска или обновления элементов. Если требуется выполнить поиск или обновление элемента, может потребоваться обход всей очереди.

Реализация стека

Сперва нам необходимо создать структуру Node, которая представляет узел очереди. Она будет иметь поля: data типа int для хранения данных (ключа) и указатель next на тип Node.

struct Node {
  int data;
  Node* next;

  Node(int data) : data(data), next(nullptr) {}
};

Теперь создадим функцию enqueue, которая будет добавлять новый элемент в конец очереди.

void enqueue(int data) {
  Node* newNode =
      new Node(data);  // Создание нового узла с переданным значением data
  if (isEmpty()) {     // Проверка, пуста ли очередь
    head = tail = newNode;  // Если очередь пуста, устанавливаем голову и хвост
                            // на новый узел
  } else {
    tail->next =
        newNode;  // Устанавливаем указатель next текущего хвоста на новый узел
    tail = newNode;  // Обновляем хвост очереди на новый узел
  }
}

Далее создадим функцию dequeue, которая будет удалять элемент из начала очереди.

void dequeue() {
  if (isEmpty()) {  // проверяем, пуста ли очередь
    std::cout << "Очередь пуста."
              << "\n";  // если очередь пуста, то выводим сообщение об ошибке и
                        // вовзвращаемся из функции
    return;
  }
  Node* temp = head;  // Создаем временный указатель и устанавливаем его на
                      // голову очереди
  head = head->next;  // Сдвигаем голову на следующий элемент
  delete temp;  // Удаляем предыдущий первый элемент
  if (head == nullptr) {
    tail =
        nullptr;  // Если после удаления головы очередь пуста, обнуляем и хвост
  }
}

Теперь создадим функцию front, которая будет возвращать значение элемента в начале очереди без его удаления, если очередь пуста, то выведется сообщение "очередь пуста".

int front() {
  if (isEmpty()) {
    std::cout << "Queue is empty."
              << "\n";
    return -1;
  }
  return head->data;  // возвращаем значение данных в головном узле
}

bool isEmpty() {
  return head ==
         nullptr;  // возвращаем true, если голова равна nullptr, иначе falsе
}

Код полностью

#include <iostream>

class Queue {
 private:
  struct Node {
    int data;
    Node* next;

    Node(int data) : data(data), next(nullptr) {}
  };

  Node* head;  // указатель на голову очереди
  Node* tail;  // указатель на хвост очереди

 public:
  Queue()
      : head(nullptr),
        tail(nullptr) {
  }  // Конструктор  Queue()  инициализирует указатели  head  и  tail  значением
     // nullptr , чтобы указать, что очередь пустая

  ~Queue() {
    while (!isEmpty()) {
      dequeue();  // Деструктор  ~Queue()  освобождает память, выделенную для
                  // каждого узла в очереди, путем последовательного вызова
                  // функции  dequeue()  до тех пор, пока очередь не станет
                  // пустой
    }
  }

  void enqueue(int data) {
    Node* newNode = new Node(data);
    if (isEmpty()) {
      head = tail = newNode;
    } else {
      tail->next = newNode;
      tail = newNode;
    }
  }

  void dequeue() {
    if (isEmpty()) {
      std::cout << "Queue is empty." << std::endl;
      return;
    }
    Node* temp = head;
    head = head->next;
    delete temp;
    if (head == nullptr) {
      tail = nullptr;
    }
  }

  int front() {
    if (isEmpty()) {
      std::cout << "Queue is empty." << std::endl;
      return -1;
    }
    return head->data;
  }

  bool isEmpty() { return head == nullptr; }
};

int main() {
  Queue queue;
  for (int i = 0; i < 3; i++) {
    queue.enqueue(i);  // добавляем числа от 0 до 2
  }

  std::cout << "Первый элемеент:  " << queue.front() << "\n";

  queue.dequeue();  // извлекаем элемент из очереди

  std::cout << "Первый элемент: " << queue.front() << "\n";

  queue.dequeue();  // извлекаем элемент из очереди
  queue.dequeue();  // извлекаем элемент из очереди

  if (queue.isEmpty()) {  // проверяем, пуста ли очередь
    std::cout << "Очередь пуста.";
  }
}

Примеры применения очереди:

  1. Обработка задач: Очередь может использоваться для управления задачами, которые должны быть выполнены в определенном порядке. Например, в операционных системах очередь может использоваться для планирования процессов или потоков выполнения.

  2. Буферизация данных: Очередь может использоваться для временного хранения данных, пока они не будут обработаны. Например, в сетевых приложениях очередь может использоваться для буферизации пакетов данных перед их обработкой или передачей.

  3. Работа с событиями: Очередь может использоваться для управления событиями или сообщениями. Например, в системах обработки событий очередь может использоваться для хранения и обработки событий в порядке их поступления.

  4. Реализация алгоритмов: Очередь может быть использована в реализации различных алгоритмов, таких как обходы графов (например, поиск в ширину), алгоритмы поиска или обработки данных.

  5. Управление ресурсами: Очередь может использоваться для управления доступом к ресурсам, чтобы обеспечить справедливое распределение ресурсов между процессами или потоками.

  6. Моделирование и симуляция: Очередь может использоваться для моделирования и симуляции различных систем или процессов, где требуется учет времени и порядка событий.

Это лишь некоторые примеры применения очереди. Фактически, очередь может быть полезной во многих сценариях, где требуется управление элементами в порядке их добавления и извлечения.

Last change: 2023-10-24, commit: d3abe52