Очередь с приоритетом

В этой статье вы познакомитесь с очередями с приоритетом и узнаете, как их реализовать в Python, Java, C и C++.

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

Значение элемента, как правило, и определяет его приоритет.

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

Разница между очередью с приоритетом и обычной очередью

Обычная очередь подчиняется принципу FIFO «первый вошел — первый вышел». В очередях с приоритетом элементы удаляются в соответствии с их приоритетом. То есть, элемент с самым высоким приоритетом удаляется из очереди в первую очередь.

Реализация очереди с приоритетом

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

Именно поэтому в этом руководстве мы будем использовать кучи. Конкретно — max-кучи.

Ниже представлена сравнительная характеристика различных реализаций очередей с приоритетом.

Операции

peek

insert

delete

Связный список

O(1)

O(1)

O(1)

Двоичная куча

O(1)

O(log n)

O(log n)

Двоичное дерево поиска

O(1)

O(log n)

O(log n)

Операции очередей с приоритетом

Основные операции очередей с приоритетом: вставка, удаление и просмотр элемента.

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

1. Вставка элемента в очередь с приоритетом

Вставка элементов в очередь с приоритетом (max-куча) происходит следующим образом:

  • Вставьте новое значение в конец дерева.

  • Отсортируйте дерево.

Алгоритм вставки элемента в очередь с приоритетом в общем виде выглядит так:

if нет узла, 
 создайте новый узел с помощью newNode.
else узел уже есть
 вставьте новый узел в конец дерева (последний узел слева — направо)
отсортируйте массив

Для min-куч этот алгоритм следует модифицировать. В них parentNode (родительский узел) всегда меньше, чем newNode (новый узел).

2. Удаление элемента из очереди с приоритетом

Удаление элемента из очереди с приоритетом происходит следующим образом:

  • Выберите элемент, который хотите удалить.

  • Поменяйте последний и удаляемый элемент местами.

  • Удалите последний элемент.

  • Отсортируйте дерево.

Алгоритм удаления элемента из очереди с приоритетом в общем виде выглядит так: 

Если узел удаляемыйУзел = конечныйУзел
 Удалить узел
Иначе поменять местами удаляемыйУзел и последнийУзел
 Удалить удаляемыйУзел

отсортировать дерево

3. Просмотр элемента в очереди с приоритетом (нахождение максимального и минимального значения)

Операция peek возвращает элемент с самым максимальным значением из max-кучи и минимальный из min-кучи.

Операция одинакова в любом случае: 

return rootNode

4. Изъятие максимального/минимального значения — extract-max/min

Extract-Max возвращает узел с максимальным значением после его удаления из max-кучи. Extract-Min — наоборот. Эта операция возвращает узел с минимальным значением после его удаления из min-кучи.

Реализация очередей с приоритетом

Python

# Реализация очередей с приоритетом в  Python

# Функция сортирует дерево
def heapify(arr, n, i):
    # Находим самое большое значение среди 
    # корневого, правого и левого дочернего элемента
    largest = i
    l = 2 * i + 1
    r = 2 * i + 2

    if l < n and arr[i] < arr[l]:
        largest = l

    if r < n and arr[largest] < arr[r]:
        largest = r

    # Меняем местами и продолжаем сортировку, 
    # если значение корневого элемента не самое большое
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)


# Функция вставляет элемент
def insert(array, newNum):
    size = len(array)
    if size == 0:
        array.append(newNum)
    else:
        array.append(newNum)
        for i in range((size // 2) - 1, -1, -1):
            heapify(array, size, i)


# Функция удаляет элемент
def deleteNode(array, num):
    size = len(array)
    i = 0
    for i in range(0, size):
        if num == array[i]:
            break

    array[i], array[size - 1] = array[size - 1], array[i]

    array.remove(size - 1)

    for i in range((len(array) // 2) - 1, -1, -1):
        heapify(array, len(array), i)


arr = []

insert(arr, 3)
insert(arr, 4)
insert(arr, 9)
insert(arr, 5)
insert(arr, 2)

print ("Массив max-кучи: " + str(arr))

deleteNode(arr, 4)
print("После удаления элемента: " + str(arr))

Java

// Реализация очередей с приоритетом в  Java

import java.util.ArrayList;

class Heap {
  // Функция сортирует дерево
  void heapify(ArrayList<Integer> hT, int i) {
    int size = hT.size();
    // Находим самое большое значение среди
    // корневого, правого и левого дочернего элемента
    int largest = i;
    int l = 2 * i + 1;
    int r = 2 * i + 2;
    if (l < size && hT.get(l) > hT.get(largest))
      largest = l;
    if (r < size && hT.get(r) > hT.get(largest))
      largest = r;

    // Меняем местами и продолжаем сортировку, 
    // если значение корневого элемента не самое большое
    if (largest != i) {
      int temp = hT.get(largest);
      hT.set(largest, hT.get(i));
      hT.set(i, temp);

      heapify(hT, largest);
    }
  }

  // Функция вставляет элемент
  void insert(ArrayList<Integer> hT, int newNum) {
    int size = hT.size();
    if (size == 0) {
      hT.add(newNum);
    } else {
      hT.add(newNum);
      for (int i = size / 2 - 1; i >= 0; i--) {
        heapify(hT, i);
      }
    }
  }

  // Функция удаляет элемент
 void deleteNode(ArrayList<Integer> hT, int num) {
    int size = hT.size();
    int i;
    for (i = 0; i < size; i++) {
      if (num == hT.get(i))
        break;
    }

    int temp = hT.get(i);
    hT.set(i, hT.get(size - 1));
    hT.set(size - 1, temp);

    hT.remove(size - 1);
    for (int j = size / 2 - 1; j >= 0; j--) {
      heapify(hT, j);
    }
  }

  // Выводим дерево в консоль
  void printArray(ArrayList<Integer> array, int size) {
    for (Integer i : array) {
      System.out.print(i + " ");
    }
    System.out.println();
  }

  // Функция main
  public static void main(String args[]) {

    ArrayList<Integer> array = new ArrayList<Integer>();
    int size = array.size();

    Heap h = new Heap();
    h.insert(array, 3);
    h.insert(array, 4);
    h.insert(array, 9);
    h.insert(array, 5);
    h.insert(array, 2);

    System.out.println("Массив max-Heap: ");
    h.printArray(array, size);

    h.deleteNode(array, 4);
    System.out.println("После удаления элемента: ");
    h.printArray(array, size);
  }
}

C

// Реализация очередей с приоритетом в  C

#include <stdio.h>
int size = 0;
void swap(int *a, int *b) {
  int temp = *b;
  *b = *a;
  *a = temp;
}

// Функция сортирует дерево
void heapify(int array[], int size, int i) {
  if (size == 1) {
    printf("В куче один элемент");
  } else {
    // Находим самое большое значение среди
   // корневого, правого и левого дочернего элемента
    int largest = i;
    int l = 2 * i + 1;
    int r = 2 * i + 2;
    if (l < size && array[l] > array[largest])
      largest = l;
    if (r < size && array[r] > array[largest])
      largest = r;

    // Меняем местами и продолжаем сортировку, 
   // если значение корневого элемента не самое большое
    if (largest != i) {
      swap(&array[i], &array[largest]);
      heapify(array, size, largest);
    }
  }
}

// Функция вставляет элемент
void insert(int array[], int newNum) {
  if (size == 0) {
    array[0] = newNum;
    size += 1;
  } else {
    array[size] = newNum;
    size += 1;
    for (int i = size / 2 - 1; i >= 0; i--) {
      heapify(array, size, i);
    }
  }
}

// Функция удаляет элемент
void deleteRoot(int array[], int num) {
  int i;
  for (i = 0; i < size; i++) {
    if (num == array[i])
      break;
  }

  swap(&array[i], &array[size - 1]);
  size -= 1;
  for (int i = size / 2 - 1; i >= 0; i--) {
    heapify(array, size, i);
  }
}

// Выводим массив в консоль
void printArray(int array[], int size) {
  for (int i = 0; i < size; ++i)
    printf("%d ", array[i]);
  printf("\n");
}

// Функция main
int main() {
  int array[10];

  insert(array, 3);
  insert(array, 4);
  insert(array, 9);
  insert(array, 5);
  insert(array, 2);

  printf("Массив max-heap: ");
  printArray(array, size);

  deleteRoot(array, 4);

  printf("После удаления элемента: ");

  printArray(array, size);
}

C++

// Реализация очередей с приоритетом в  C++

#include <iostream>
#include <vector>
using namespace std;

// Функция меняет элементы местами
void swap(int *a, int *b) {
  int temp = *b;
  *b = *a;
  *a = temp;
}

// Функция сортирует
void heapify(vector<int> &hT, int i) {
  int size = hT.size();
  
  // Находим самое большое значение среди 
  // корневого, правого и левого дочернего элемента
  int largest = i;
  int l = 2 * i + 1;
  int r = 2 * i + 2;
  if (l < size && hT[l] > hT[largest])
    largest = l;
  if (r < size && hT[r] > hT[largest])
    largest = r;

  // Меняем местами и продолжаем сортировку,
  // если значение корневого элемента не самое большое
  if (largest != i) {
    swap(&hT[i], &hT[largest]);
    heapify(hT, largest);
  }
}

// Функция вставляет элемент
void insert(vector<int> &hT, int newNum) {
  int size = hT.size();
  if (size == 0) {
    hT.push_back(newNum);
  } else {
    hT.push_back(newNum);
    for (int i = size / 2 - 1; i >= 0; i--) {
      heapify(hT, i);
    }
  }
}

// Функция удаляет элемент
void deleteNode(vector<int> &hT, int num) {
  int size = hT.size();
  int i;
  for (i = 0; i < size; i++) {
    if (num == hT[i])
      break;
  }
  swap(&hT[i], &hT[size - 1]);

  hT.pop_back();
  for (int i = size / 2 - 1; i >= 0; i--) {
    heapify(hT, i);
  }
}

// Выводим дерево в консоль
void printArray(vector<int> &hT) {
  for (int i = 0; i < hT.size(); ++i)
    cout << hT[i] << " ";
  cout << "\n";
}

// Функция main
int main() {
  vector<int> heapTree;

  insert(heapTree, 3);
  insert(heapTree, 4);
  insert(heapTree, 9);
  insert(heapTree, 5);
  insert(heapTree, 2);

  cout << "Массив max-heap: ";
  printArray(heapTree);

  deleteNode(heapTree, 4);

  cout << "После удаления элемента: ";

  printArray(heapTree);
}

Где применяются очереди с приоритетом

  • Алгоритм Дейкстры.
  • Реализация стека.
  • Балансировка нагрузки и обработка прерываний в операционных системах.
  • Сжатие данных в коде Хаффмана. 
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©