Моя структура данных требует трех операций:
- вставить элемент в произвольном месте в порядке
- найти и удалить наименьший элемент
- (редко) удалить элемент с помощью некоторого ключа, возвращенного во время вставки
Существующий код представляет собой односвязный список и выполняет линейный поиск, чтобы найти точку вставки. O (n).
Поиск и удаление наименьшего элемента тривиален: снимите и утилизируйте головную тягу. O (1).
Вставка возвращает указатель на ссылку, и вызов удаления удаляет этот указатель. Если бы это был двойной список, ссылка могла бы быть просто удалена. O (1). Увы, список односвязный, и в списке ищется узел этого адреса, поэтому это O (n). Этот поиск дорог, но в некоторых случаях он позволяет обнаружить попытку удаления узла дважды: попытка удаления узла, которого нет в списке, не найдет его, поэтому ничего не будет делать, кроме как сгенерировать предупреждение в журнале , С другой стороны, узлы хранятся в пуле памяти LIFO, поэтому, вероятно, будут использоваться повторно, поэтому случайное повторное удаление узла может вместо этого удалить некоторый другой узел.)
OK, с кучей , вставка O (log n). Удаление минимума - O (log n). И то, и другое просто.
Но как насчет удаления по ключу? Если я храню кучу в массиве, это в основном линейный поиск, O (n). Я перемещаю элементы в куче, чтобы сохранить свойство кучи (пузырясь вниз и вверх по мере необходимости), поэтому я не могу просто использовать адрес узла. Кроме того, если вы не примете фиксированный максимальный размер, вам нужно перераспределить массив, который обычно перемещает его.
Я думаю, что, возможно, куча может быть массивом указателей на фактические узлы, которые живут в другом месте. У каждого узла будет свой индекс массива, и когда я перемещаю указатели на узлы в куче, я обновляю узел новым индексом массива. Таким образом, запрос на удаление узла может предоставить мне этот узел. Я использую сохраненный индекс узла в куче и удаляю этот указатель, так что теперь log (N). Это кажется гораздо более сложным.
Учитывая дополнительные накладные расходы по выделению неподвижных узлов по отдельности и обновлению их поля индекса массива, звучит так, как будто это может быть больше, чем какое-то очень редкое количество линейных поисков. OTOH, преимущество сохранения узлов отдельно от кучи массива состоит в том, что быстрее менять местами указатели, чем целые узлы (которые в моем случае могут быть 32 байта или более).
Любые более простые идеи?