Как удалить элементы из двоичной кучи?

Как я понимаю, binary heap не поддерживает удаление случайных элементов. Что делать, если мне нужно удалить случайные элементы из binary heap?

Очевидно, я могу удалить элемент и переустановить всю кучу в O(N). Могу ли я сделать лучше?

Ответ 1

Да и нет.

Проблема заключается в двоичной куче, которая не поддерживает поиск для произвольного элемента. Поиск сам по себе O(n).

Однако, если у вас есть указатель на элемент (и не только его значение) - , вы можете поменять элемент с помощью самого правильного листа, удалите этот лист, а затем повторно запустите -heap (путем отсеивания вновь размещенного элемента столько, сколько необходимо). Это приводит к удалению O(logn), но требует указателя на фактический элемент, который вы ищете.

Ответ 2

Амит прав в своем ответе, но вот еще один нюанс:

позиция удаляемого элемента (где вы помещаете правый лист) может потребоваться, чтобы пузыряться (сравните с родителем и двигайтесь вверх, пока родитель больше вас). Иногда требуется пузыриться (сравните с детьми и двигайтесь вниз, пока все дети не станут меньше вас). Все зависит от случая.

Ответ 3

Зависит от того, что подразумевается под "случайным элементом". Если это означает, что куча содержит элементы [e1, e2,..., eN] и хочет удалить некоторое ei (1 <= я <= N), тогда это возможно.

Если вы используете реализацию двоичной кучи из какой-либо библиотеки, возможно, она не предоставляет вам API, который вам нужен. В этом случае вы должны искать другую библиотеку, которая имеет его.

Если вы должны были реализовать его самостоятельно, вам понадобятся два дополнительных вызова:

  • Процедура deleteAtIndex (heap, i), которая удаляет node по индексу i путем позиционирования последнего элемента в массиве кучи в i, уменьшая количество элементов и, наконец, перетасовка вниз/вверх новый i-й элемент для поддержания инварианта кучи. Большинство Обычное использование этой процедуры заключается в том, чтобы "выскочить" из кучи, вызвав deleteAtIndex (heap, 1) - при условии индексации 1-начала. Эта операция будет работать O (log n) (хотя, чтобы быть полным, я отмечу, что самая высокая граница может быть улучшено до O (log (log n)) в зависимости от некоторых допущений о ключах ваших элементов.

  • Процедура deleteElement (heap, e), которая удаляет элемент e (ваш произвольный элемент). Ваш алгоритм кучи будет поддерживать массив ElementIndex таким образом, чтобы ElementIndex [e] возвращался текущий индекс элемента e: вызов deleteAtIndex (heap, ElementIndex [e]) будет делать то, что вы хотите. Он также будет работать в O (log n), поскольку доступ к массиву постоянный.

Так как двоичные кучи часто используются в алгоритмах, которые просто выставляют наивысший (или самый низкий) (а не удалять произвольные элементы), я полагаю, что некоторые библиотеки могут пропустить API-интерфейс deleteAtIndex для экономии места (дополнительный массив ElementIndex, упомянутый выше).