Очередь из контейнеров — это структура данных , реализующая абстрактный тип данных очереди приоритетов : она поддерживает динамическую коллекцию элементов с числовыми приоритетами и обеспечивает быстрый доступ к элементу с минимальным (или максимальным) приоритетом. В очереди из контейнеров приоритеты должны быть целыми числами , и она особенно подходит для приложений, в которых приоритеты имеют небольшой диапазон. [1] Очередь из контейнеров имеет форму массива контейнеров: массива данных , индексированного по приоритетам, ячейки которого содержат коллекции элементов с одинаковым приоритетом. При использовании этой структуры данных вставка элементов и изменение их приоритета занимают постоянное время . Поиск и удаление элемента с минимальным приоритетом занимает время, пропорциональное количеству контейнеров или, при сохранении указателя на последний найденный контейнер, время, пропорциональное разнице в приоритетах между последовательными операциями.
Очередь с ведром — это аналог приоритетной очереди сортировки Pigeonhole (также называемой сортировкой с ведром), алгоритма сортировки, который помещает элементы в ведры, индексированные по их приоритетам, а затем объединяет ведры. Использование очереди с ведром в качестве приоритетной очереди в сортировке выбором дает форму алгоритма сортировки Pigeonhole. [2] Очереди с ведром также называются очередями с ведром с приоритетом [3] или очередями с приоритетом ограниченной высоты . [1] При использовании для квантованных приближений к действительным числовым приоритетам их также называют неаккуратными очередями с приоритетом [4] или псевдоприоритетными очередями . [5] Они тесно связаны с календарной очередью , структурой, которая использует аналогичный массив ведров для точной приоритезации действительными числами.
Приложения очереди из ведер включают вычисление вырожденности графа , быстрые алгоритмы для кратчайших путей и широчайших путей для графов с весами, которые являются небольшими целыми числами или уже отсортированы, и жадные алгоритмы приближения для задачи покрытия множества . Квантованная версия структуры также применялась к планированию [2] и к марширующим кубам в компьютерной графике . [4] Первое использование очереди из ведер [6] было в алгоритме кратчайшего пути Дайала (1969). [7]
Очередь контейнеров может обрабатывать элементы с целочисленными приоритетами в диапазоне от 0 или 1 до некоторой известной границы C , а также операции, которые вставляют элементы, изменяют приоритет элементов или извлекают (находят и удаляют) элемент, имеющий минимальный (или максимальный) приоритет. Она состоит из массива A структур данных контейнера ; в большинстве источников эти контейнеры представляют собой двусвязные списки, но в качестве альтернативы они могут быть динамическими массивами [3] или динамическими наборами . Контейнер в ячейке массива p [ p ] хранит коллекцию элементов, приоритет которых равен p .
Очередь контейнеров может обрабатывать следующие операции:
Таким образом, вставки и изменения приоритета занимают постоянное время, а извлечение элемента с минимальным или максимальным приоритетом занимает время O ( C ) . [1] [6] [8]
В качестве оптимизации структура данных может начинать каждый последовательный поиск непустого контейнера с последнего найденного непустого контейнера, а не с начала массива. Это можно сделать двумя способами: ленивым (откладывая эти последовательные поиски до тех пор, пока они не понадобятся) или жадным (выполняя поиск заранее). Выбор времени выполнения поиска влияет на то, какие из операций структуры данных замедляются этими поисками. Первоначальная версия структуры Dial использовала ленивый поиск. Это можно сделать, поддерживая индекс L , который является нижней границей минимального приоритета любого элемента, находящегося в очереди в данный момент. При вставке нового элемента L следует обновить до минимума его старого значения и приоритета нового элемента. При поиске элемента с минимальным приоритетом поиск может начинаться с L вместо нуля, а после поиска L следует оставить равным приоритету, который был найден в ходе поиска. [7] [9] В качестве альтернативы жадная версия этой оптимизации сохраняет L обновленным, так что он всегда указывает на первый непустой контейнер. При вставке нового элемента с приоритетом, меньшим, чем L , структура данных устанавливает L на новый приоритет, а при удалении последнего элемента из контейнера с приоритетом L выполняет последовательный поиск по большим индексам, пока не найдется непустой контейнер и не установит L на приоритет полученного контейнера. [1]
В любом из этих двух вариантов каждый последовательный поиск занимает время, пропорциональное разнице между старым и новым значениями L . Это может быть значительно быстрее, чем ограничение по времени O ( C ) для поисков в неоптимизированной версии структуры данных. Во многих приложениях приоритетных очередей, таких как алгоритм Дейкстры , минимальные приоритеты образуют монотонную последовательность , что позволяет использовать монотонную приоритетную очередь . В этих приложениях, как для ленивых, так и для энергичных вариаций оптимизированной структуры, последовательные поиски непустых ведер охватывают непересекающиеся диапазоны ведер. Поскольку каждое ведро находится не более чем в одном из этих диапазонов, их количество шагов в сумме составляет не более C . Следовательно, в этих приложениях общее время для последовательности из n операций составляет O ( n + C ) , а не более медленное ограничение по времени O ( nC ), которое было бы получено без этой оптимизации. [9] Соответствующая оптимизация может быть применена в приложениях, где очередь контейнеров используется для поиска элементов с максимальным приоритетом, но в этом случае она должна поддерживать индекс, который ограничивает максимальный приоритет сверху, а последовательный поиск непустого контейнера должен осуществляться сверху вниз от этой верхней границы. [10]
Другая оптимизация (уже предложенная Дайалом 1969) может быть использована для экономии места, когда приоритеты монотонны и на протяжении всего алгоритма всегда попадают в диапазон значений r , а не простираются на весь диапазон от 0 до C. В этом случае можно индексировать массив по приоритетам по модулю r, а не по их фактическим значениям. Поиск элемента с минимальным приоритетом всегда должен начинаться с предыдущего минимума, чтобы избежать приоритетов, которые выше минимума, но имеют меньшие модули. В частности, эта идея может быть применена в алгоритме Дейкстры на графах, длины ребер которых являются целыми числами в диапазоне от 1 до r . [8]
Поскольку создание новой очереди ведер включает инициализацию массива пустых ведер, этот шаг инициализации занимает время, пропорциональное количеству приоритетов. Разновидность очереди ведер, описанная Дональдом Б. Джонсоном в 1981 году, вместо этого сохраняет только непустые ведра в связанном списке, отсортированном по их приоритетам, и использует вспомогательное дерево поиска для быстрого нахождения позиции в этом связанном списке для любых новых ведер. Требуется время O (log log C ) для инициализации этой вариантной структуры, постоянное время для поиска элемента с минимальным или максимальным приоритетом и время O (log log D ) для вставки или удаления элемента, где D — это разница между ближайшим меньшим и большим приоритетами к приоритету вставленного или удаленного элемента. [11]
Например, рассмотрим очередь контейнеров с четырьмя приоритетами, числами 0, 1, 2 и 3. Она состоит из массива, четыре ячейки которого содержат набор элементов, изначально пустых. Для целей этого примера можно записать как последовательность из четырех наборов в скобках: . Рассмотрим последовательность операций, в которой мы вставляем два элемента и с тем же приоритетом 1, вставляем третий элемент с приоритетом 3, меняем приоритет на 3, а затем выполняем два извлечения элемента с минимальным приоритетом.
Очередь из блоков может использоваться для поддержания вершин неориентированного графа , упорядоченных по их степеням , и многократного поиска и удаления вершины минимальной степени. [1] Этот жадный алгоритм может использоваться для вычисления вырожденности заданного графа, равной наибольшей степени любой вершины на момент ее удаления. Алгоритм занимает линейное время , с оптимизацией или без нее, которая поддерживает нижнюю границу минимального приоритета, поскольку каждая вершина находится за время, пропорциональное ее степени, а сумма всех степеней вершин линейна по числу ребер графа. [12]
В алгоритме Дейкстры для кратчайших путей в ориентированных графах с весами ребер, которые являются положительными целыми числами, приоритеты являются монотонными, [13] и монотонная очередь из блоков может быть использована для получения временного ограничения O ( m + dc ) , где m — количество ребер, d — диаметр сети, а c — максимальная (целочисленная) стоимость связи. [9] [14] Этот вариант алгоритма Дейкстры также известен как алгоритм Дайала , [9] в честь Роберта Б. Дайала, который опубликовал его в 1969 году. [7] Та же идея также работает, используя квантованную очередь из блоков, для графов с положительными действительными весами ребер, когда отношение максимального веса к минимальному не превышает c . [15] В этой квантованной версии алгоритма вершины обрабатываются не в порядке, по сравнению с результатом с неквантованной очередью с приоритетами, но правильные кратчайшие пути все равно находятся. [5] В этих алгоритмах приоритеты будут охватывать только диапазон шириной c + 1 , поэтому модульную оптимизацию можно использовать для сокращения пространства до O ( n + c ) . [8] [14]
Вариант того же алгоритма может быть использован для задачи поиска самого широкого пути . В сочетании с методами быстрого разбиения нецелочисленных весов ребер на подмножества, которым можно назначить целочисленные приоритеты, это приводит к решениям с почти линейным временем для версии задачи поиска самого широкого пути с одним источником и одним пунктом назначения. [16]
Задача покрытия множеств имеет в качестве входных данных семейство множеств . Выходными данными должно быть подсемейство этих множеств с тем же объединением, что и исходное семейство, включающее как можно меньше множеств. Она NP-трудна , но имеет жадный алгоритм аппроксимации , который достигает логарифмического отношения аппроксимации, по сути, наилучшего возможного, если только P = NP . [17] Этот алгоритм аппроксимации выбирает свое подсемейство, многократно выбирая множество, которое покрывает максимально возможное количество оставшихся непокрытых элементов. [18] Стандартное упражнение по проектированию алгоритмов требует реализации этого алгоритма, которая занимает линейное время по размеру входных данных, который является суммой размеров всех входных множеств. [19]
Это можно решить с помощью очереди наборов в семействе входных данных, упорядоченных по числу оставшихся элементов, которые они покрывают. Каждый раз, когда жадный алгоритм выбирает набор в качестве части своего вывода, вновь охваченные элементы набора должны вычитаться из приоритетов других наборов, которые их покрывают; в ходе работы алгоритма количество этих изменений приоритетов равно просто сумме размеров входных наборов. Приоритеты представляют собой монотонно убывающие целые числа, ограниченные сверху числом элементов, которые должны быть охвачены. Каждый выбор жадного алгоритма включает в себя поиск набора с максимальным приоритетом, что можно сделать путем сканирования вниз по контейнерам очереди контейнеров, начиная с самого последнего предыдущего максимального значения. Общее время линейно зависит от размера входных данных. [10]
Очереди ковша можно использовать для планирования задач с крайними сроками, например, при пересылке пакетов для интернет-данных с гарантиями качества обслуживания . Для этого приложения крайние сроки должны быть квантованы в дискретные интервалы, а задачи, крайние сроки которых попадают в один и тот же интервал, считаются имеющими эквивалентный приоритет. [2]
Разновидность квантованной структуры данных очереди ведер, календарная очередь , была применена для планирования дискретно-событийных симуляций , где элементы в очереди являются будущими событиями, приоритет которых определяется временем в симуляции, когда события должны произойти. В этом приложении порядок событий имеет решающее значение, поэтому приоритеты не могут быть аппроксимированы. Поэтому календарная очередь выполняет поиск элемента с минимальным приоритетом иным способом, чем очередь ведер: в очереди ведер может быть возвращен любой элемент первого непустого ведра, но вместо этого календарная очередь ищет все элементы в этом ведерке, чтобы определить, какой из них имеет наименьший неквантованный приоритет. Чтобы поддерживать эти поиски быстрыми, эта вариация пытается поддерживать количество ведер пропорциональным количеству элементов, регулируя масштаб квантования и перестраивая структуру данных, когда она выходит из равновесия. Календарные очереди могут быть медленнее очередей с контейнерами в худшем случае (когда множество элементов попадают в один и тот же наименьший контейнер), но они быстры, когда элементы равномерно распределены по контейнерам, в результате чего средний размер контейнера остается постоянным. [20] [21]
В прикладной математике и численных методах решения дифференциальных уравнений неаккуратные очереди приоритетов использовались для расстановки приоритетов шагов метода быстрого марша для решения краевых задач уравнения Эйконала , используемого для моделирования распространения волн . Этот метод находит моменты времени, в которые движущаяся граница пересекает набор дискретных точек (например, точек целочисленной сетки), используя метод расстановки приоритетов, напоминающий непрерывную версию алгоритма Дейкстры , и его время выполнения доминирует его очередь приоритетов этих точек. Его можно ускорить до линейного времени, округлив приоритеты, используемые в этом алгоритме, до целых чисел и используя очередь контейнеров для этих целых чисел. Как и в алгоритмах Дейкстры и Дайала, приоритеты монотонны, поэтому быстрый марш может использовать монотонную оптимизацию очереди контейнеров и ее анализ. Однако дискретизация вносит некоторую ошибку в полученные вычисления. [4]