Это не домашняя работа.
Я использую малочисленную "приоритетную очередь" (реализованный как массив в данный момент) для хранения последних объектов N с самым маленьким значением. Это немного медленно - O (N) время вставки объекта. Текущая реализация отслеживает самый большой объект в массиве и отбрасывает любые объекты, которые не вписались бы в массив, но я все еще хотел бы сократить количество операций далее.
поиск приоритетного алгоритма очереди, который соответствует следующим требованиям:
Я первоначально думал об использовании двоичной "кучи" (они могут быть легко реализованы через массивы), но по-видимому они не ведут себя хорошо, когда массив не может больше расти. Связанные списки и массивы потребуют дополнительного времени для того, чтобы переместить вещи. приоритетная очередь stl выращивает и использует динамическое выделение (я могу быть неправ относительно этого, хотя).
Так, какие-либо другие идеи?
Править--
Я не интересуюсь реализацией STL. Реализация STL (предложенный несколькими людьми) работает немного медленнее, чем в настоящее время используемая линейная матрица из-за высокого количества вызовов функции.
Я интересуюсь приоритетными алгоритмами очереди, не implemnetations.
Кучи на основе массивов кажутся идеальными для вашей цели. Я не уверен, почему вы отвергли их.
Вы используете максимальную кучу.
Скажем, у вас есть куча из N элементов (реализованная как массив), которая содержит N наименьших элементов, видимых до сих пор.
Когда приходит элемент, вы проверяете его на соответствие max (время O(1)), и отбрасываете, если он больше.
Если поступающее значение меньше, вы изменяете корень на новое значение и отсеиваете вниз это измененное значение - в худшем случае время O(log N).
Процесс отсеивания прост: Начиная с корня, на каждом шаге вы меняете это значение с его большим дочерним значением, пока свойство max-heap не будет восстановлено.
Таким образом, вам не придется делать никаких удалений, которые, вероятно, придется делать, если вы используете std::priority_queue. В зависимости от реализации std::priority_queue, это может привести к выделению/деаллокации памяти.
Итак, вы можете иметь следующий код:
В среднем, однако, вам, вероятно, не придется отсеивать новое значение по всему пути вниз, и вы можете получить лучше, чем O(logn) среднее время вставки (хотя я не пытался доказать это).
Вы выделяете массив размера N только один раз, и любая вставка выполняется путем обмена элементами массива, так что после этого нет динамического выделения памяти.
Посмотрите на странице вики, где есть псевдокод для heapify и sift-down: http://en.wikipedia.org/wiki/Heapsort
Используйте std :: priority_queue
с наибольшим элементом во главе. Для каждого нового элемента отбрасывайте его, если он > =
элемент заголовка, в противном случае вытяните элемент заголовка и вставьте новый элемент.
Примечание: стандартные контейнеры будут расти, только если вы заставите их расти. Пока вы удалите один элемент перед вставкой нового элемента (конечно, после того, как он достигнет максимального размера), этого не произойдет.
Большинство приоритетных очередей, с которыми я работаю, основаны на связанных списках. Если у вас есть заранее определенное количество уровней приоритета, вы можете легко создать очередь приоритетов с вставкой O (1), имея массив связанных списков - один связанный список на уровень приоритета. Элементы с одинаковым приоритетом, конечно, будут преобразованы в FIFO, но это можно считать приемлемым.
Добавление и удаление затем становится чем-то вроде (ваш API может отличаться) ...
listItemAdd (&list[priLevel], &item); /* Add to tail */
pItem = listItemRemove (&list[priLevel]); /* Remove from head */
Получение первого элемента в очереди становится проблемой поиска непустого связанного списка с наивысшим приоритетом. Это может быть O (N), но есть несколько уловок, которые вы можете использовать для его ускорения.
Надеюсь, это поможет.
Если количество приоритетов невелико и фиксировано, вы можете использовать кольцевой буфер для каждого приоритета. Это приведет к потере пространства, если объекты большие, но если их размер сопоставим с указателем / индексом, то варианты с сохранением дополнительных указателей в объектах могут таким же образом увеличить размер массива.
Или вы можете использовать простой односвязный список внутри массива и хранить 2 * M + 1 указателя / индекса, один будет указывать на первый свободный узел, а другие пары будут указывать на начало и конец каждого приоритета. В этом случае вам придется сравнивать в среднем. O (M) перед удалением следующего узла с помощью O (1). И вставка займет O (1).
Если вы создадите приоритетную очередь STL с максимальным размером (возможно, из вектора, инициализированного заполнителями), а затем проверите размер перед вставкой (удалив элемент, если необходимо, заранее), у вас никогда не будет динамического распределения во время операций вставки. Реализация STL довольно эффективна.
Нашел решение («разница» означает «приоритет» в коде, а maxRememberedResults - 255 (может быть любым (2 ^ n - 1)):
template <typename T> inline void swap(T& a, T& b){
T c = a;
a = b;
b = c;
}
struct MinDifferenceArray{
enum{maxSize = maxRememberedResults};
int size;
DifferenceData data[maxSize];
void add(const DifferenceData& val){
if (size >= maxSize){
if(data[0].difference <= val.difference)
return;
data[0] = val;
for (int i = 0; (2*i+1) < maxSize; ){
int next = 2*i + 1;
if (data[next].difference < data[next+1].difference)
next++;
if (data[i].difference < data[next].difference)
swap(data[i], data[next]);
else
break;
i = next;
}
}
else{
data[size++] = val;
for (int i = size - 1; i > 0;){
int parent = (i-1)/2;
if (data[parent].difference < data[i].difference){
swap(data[parent], data[i]);
i = parent;
}
else
break;
}
}
}
void clear(){
size = 0;
}
MinDifferenceArray()
:size(0){
}
};
И мы получаем O (log (N)) insert как наихудший сценарий.
Это то же самое решение, которое предоставил пользователь с ником "Moron". Спасибо всем за ответы.
P.S. Очевидно, программирование без сна - не лучшая идея.
Matters Computational см. Стр. 158. Сама реализация довольно хороша, и вы даже можете немного подправить ее, не делая ее менее читаемой. Например, когда вы вычисляете левый дочерний элемент, например:
int left = i / 2;
Вы можете вычислить правого ребенка следующим образом:
int right = left + 1;