Очередь с приоритетом
Описание
Очередь с приоритетом (priority queue) - это абстрактный тип данных, похожий на обычную очередь (см. часть 1, раздел 3) или стек (см. часть 1, раздел 4), за исключением того, что в ней каждый элемент имеет определенный "приоритет" (priority). Элемент с более высоким приоритетом обрабатывается перед элементом с более низким приоритетом. Если два элемента имеют одинаковый приоритет, они обрабатываются в порядке их расположения в очереди.
Несмотря на то, что очереди с приоритетом часто реализуются с помощью куч, они концептуально различаются. Очередь с приоритетом - это абстрактная концепция, вроде "списка" (list) или "карты" (map). Как список может быть реализован с помощью связного списка (см. часть 1, раздел 1) или массива, так и очередь с приоритетом может быть реализована с помощью кучи или другим способом, например, с помощью неупорядоченного массива.
Интерактивную визуализации очереди с приоритетом можно посмотреть здесь.
Сложность
Временная сложность очереди с приоритетом составляет O(n) или O(n*log(n)) (зависит от реализации).
Реализация
Для реализации очереди с приоритетом мы воспользуемся min-кучей (см. предыдущий раздел).
Начнем с конструктора:
// data-structures/priority-queue.js
// Импортируем конструктор функции сравнения узлов
import Comparator from '../utils/comparator'
// Импортируем конструктор min-кучи
import MinHeap from './heap/min-heap'
// Очередь с приоритетом.
// Реализация на основе min-кучи
export default class PriorityQueue extends MinHeap {
constructor() {
// Инициализируем min-кучу
super()
// Карта приоритетов
this.priorities = new Map()
// Функция сравнения элементов
this.compare = new Comparator(this.comparePriorities.bind(this))
}
}
Методы добавления и удаления элементов в/из очереди:
// Добавляет элемент в очередь.
// Принимает элемент и приоритет.
// Чем больше приоритет (меньше значение `priority`),
// тем "выше" элемент находится в очереди
add(item, priority = 0) {
// Обновляем приоритеты
this.priorities.set(item, priority)
// Добавляем элемент в кучу
super.add(item)
return this
}
// Удаляет элемент из очереди.
// Принимает элемент и кастомную функцию сравнения элементов
remove(item, compare) {
// Удаляем элемент из кучи
super.remove(item, compare)
// Обновляем приоритеты
this.priorities.delete(item)
return this
}
Метод обновления приоритета:
// Обновляет приоритет.
// Принимает элемент и новый приоритет
changePriority(item, priority) {
// Удаляем элемент из очереди
this.remove(item, new Comparator(this.compareValues))
// Добавляем элемент с новым приоритетом
this.add(item, priority)
return this
}
Метод поиска элемента по значению и определения наличия элемента:
// Ищет элемент по значению.
// Возвращает массив индексов
findByValue(item) {
return this.find(item, new Comparator(this.compareValues))
}
// Определяет наличие элемента
hasValue(item) {
return this.findByValue(item).length > 0
}
Методы сравнения элементов по приоритетам и значениям:
// Сравнивает приоритеты
comparePriorities(a, b) {
// Вызываем функцию сравнения значений,
// передавая ей приоритеты
return this.compareValues(this.priorities.get(a), this.priorities.get(b))
}
// Сравнивает значения
compareValues(a, b) {
if (a === b) {
return 0
}
return a < b ? -1 : 1
}
Полный код очереди с приоритетом
Тесты
Запускаем тесты:
npm run test ./data-structures/__tests__/priority-queue
