Очередь с приоритетом
В этой статье вы познакомитесь с очередями с приоритетом и узнаете, как их реализовать в Python, Java, C и C++.
Очереди с приоритетом — разновидность очередей, в которой у каждого элемента есть свой приоритет. Обслуживаются они в соответствии со своими приоритетом. Если у элементов одинаковый приоритет, то обслуживаются они по их порядку в очереди.
Значение элемента, как правило, и определяет его приоритет.
То есть, у элемента с самым большим значением самый высокий приоритет. Правда, это не всегда так. Самый высокий приоритет может быть у элемента и с самым малым значением. В остальных случаях мы можем задавать приоритеты элементам по своему усмотрению.
Разница между очередью с приоритетом и обычной очередью
Обычная очередь подчиняется принципу FIFO «первый вошел — первый вышел». В очередях с приоритетом элементы удаляются в соответствии с их приоритетом. То есть, элемент с самым высоким приоритетом удаляется из очереди в первую очередь.
Реализация очереди с приоритетом
Очереди с приоритетом можно реализовать с помощью следующих структур данных: массив, связный список, куча и двоичное дерево поиска. Среди всех этих структур выделяется куча — это самый эффективный способ реализации очереди с приоритетом.мы
Именно поэтому в этом руководстве мы будем использовать кучи. Конкретно — max-кучи.
Ниже представлена сравнительная характеристика различных реализаций очередей с приоритетом.
Операции |
peek |
insert |
delete |
Связный список |
|
|
|
Двоичная куча |
|
|
|
Двоичное дерево поиска |
|
|
|
Операции очередей с приоритетом
Основные операции очередей с приоритетом: вставка, удаление и просмотр элемента.
Перед изучением очередей с приоритетом настоятельно рекомендуем почитать о кучах. Это нужно для того, чтобы лучше понимать двоичные кучи. В этой статье именно их мы используем для реализации очередей с приоритетом.
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);
}
Где применяются очереди с приоритетом
- Алгоритм Дейкстры.
- Реализация стека.
- Балансировка нагрузки и обработка прерываний в операционных системах.
- Сжатие данных в коде Хаффмана.