Это, вероятно, приходит к вам немного позже в игре, что касается вашего вопроса, но давайте закончим.
Тестирование — лучший способ ответить на этот вопрос для конкретной компьютерной архитектуры, компилятора и реализации. Кроме того, есть обобщения.
Во-первых, приоритетные очереди не обязательно равны O(n log n).
Если у вас есть целочисленные данные, есть приоритетные очереди, которые работают за время O (1). Публикация Бойхера и Мейера 1992 года «Морфологический подход к сегментации: преобразование водораздела» описывает иерархические очереди, которые работают довольно быстро для целочисленных значений с ограниченным диапазоном. Публикация Брауна 1988 года «Очереди календаря: быстрая реализация очереди с приоритетом 0 (1) для задачи набора событий симуляции» предлагает другое решение, которое хорошо работает с большими диапазонами целых чисел — два десятилетия работы после публикации Брауна дали некоторые хорошие результаты для решения целочисленных задач. очереди с приоритетом быстро. Но механизм этих очередей может усложниться: сортировка ведрами и сортировка по основанию могут по-прежнему обеспечивать операцию O(1). В некоторых случаях вы можете даже квантовать данные с плавающей запятой, чтобы воспользоваться преимуществами очереди с приоритетом O(1).
Даже в общем случае данных с плавающей запятой значение O(n log n) немного вводит в заблуждение. В книге Эделькампа «Эвристический поиск: теория и приложения» есть следующая удобная таблица, показывающая временную сложность для различных алгоритмов очереди с приоритетом (помните, очереди с приоритетом эквивалентны сортировке и управлению кучей):
![Приоритетные сложности времени ожидания в очереди](https://i.stack.imgur.com/Kzm5t.png)
Как видите, многие очереди с приоритетом требуют затрат O(log n) не только на вставку, но и на извлечение и даже на управление очередью! Хотя коэффициент обычно не используется для измерения временной сложности алгоритма, эти затраты все же стоит знать.
Но все эти очереди по-прежнему имеют сравнимую временную сложность. Что лучше? Этот вопрос рассматривается в статье Криса Л. Луенго Хендрикса 2010 года, озаглавленной «Пересмотр приоритетных очередей для анализа изображений».
![Время ожидания для приоритетных очередей](https://i.stack.imgur.com/zHm2c.png)
В ходе теста Хендрикса приоритетная очередь была заполнена N случайными числами в диапазоне [0,50]. Затем самый верхний элемент очереди удалялся из очереди, увеличивался на случайное значение в диапазоне [0,2], а затем помещался в очередь. Эта операция повторялась 10^7 раз. Накладные расходы на генерацию случайных чисел вычитались из измеренного времени. Лестничные очереди и иерархические кучи показали себя в этом тесте достаточно хорошо.
Также было измерено время инициализации и очистки очередей для каждого элемента --- эти тесты очень важны для вашего вопроса.
![Поэлементная постановка в очередь и время удаления из очереди](https://i.stack.imgur.com/nehNB.png)
Как видите, разные очереди часто по-разному реагировали на постановку в очередь и удаление из очереди. Эти цифры подразумевают, что, хотя могут существовать алгоритмы очереди с приоритетом, которые лучше подходят для непрерывной работы, нет лучшего выбора алгоритма для простого заполнения, а затем опустошения очереди с приоритетом (операции, которую вы выполняете).
Давайте вернемся к вашим вопросам:
Что быстрее: вставка в приоритетную очередь или ретроспективная сортировка?
Как показано выше, очереди с приоритетами можно сделать эффективными, но затраты на их вставку, удаление и управление все же остаются. Вставка в вектор выполняется быстро. Это O (1) в амортизированном времени, и нет никаких затрат на управление, плюс вектор O (n), который нужно прочитать.
Сортировка вектора будет стоить вам O(n log n) при условии, что у вас есть данные с плавающей запятой, но на этот раз сложность не скрывает такие вещи, как очереди с приоритетами. (Тем не менее, вы должны быть немного осторожны. Быстрая сортировка очень хорошо работает с некоторыми данными, но в худшем случае она имеет временную сложность O (n ^ 2). Для некоторых реализаций это серьезная угроза безопасности.)
Боюсь, у меня нет данных о затратах на сортировку, но я бы сказал, что ретроактивная сортировка отражает суть того, что вы пытаетесь сделать лучше, и поэтому является лучшим выбором. Основываясь на относительной сложности управления приоритетной очередью по сравнению с пост-сортировкой, я бы сказал, что пост-сортировка должна быть быстрее. Но опять же, вы должны проверить это.
Я создаю некоторые элементы, которые мне нужно отсортировать в конце. Мне было интересно, что быстрее с точки зрения сложности: вставлять их прямо в приоритетную очередь или аналогичную структуру данных или использовать алгоритм сортировки в конце?
Мы, вероятно, рассмотрели это выше.
Однако есть еще один вопрос, который вы не задали. И, возможно, вы уже знаете ответ. Это вопрос стабильности. C++ STL говорит, что приоритетная очередь должна поддерживать "строгий слабый" порядок. Это означает, что элементы с одинаковым приоритетом несравнимы и могут располагаться в любом порядке, в отличие от «общего порядка», когда каждый элемент сопоставим. (Есть хорошее описание упорядочения здесь.) , «строгий слабый» аналогичен нестабильной сортировке, а «полный порядок» аналогичен стабильной сортировке.
В результате, если элементы с одинаковым приоритетом должны оставаться в том же порядке, в котором вы их вставили в свою структуру данных, вам нужна стабильная сортировка или общий порядок. Если вы планируете использовать C++ STL, у вас есть только один вариант. Очереди с приоритетами используют строгий слабый порядок, поэтому здесь они бесполезны, но алгоритм «stable_sort» в библиотеке алгоритмов STL выполнит свою работу.
Надеюсь, это поможет. Дайте мне знать, если вам нужна копия любого из упомянутых документов или вы хотели бы получить разъяснения. :-)
person
Richard
schedule
25.05.2012