Что привело меня к разработке этого решения (который я не 100 % sure is correct) заключается в том, что я действительно нашел решение для удаления любого узла в куче min-max, но это неправильно.
Неверное решение можно найти here (реализовано на C++) и here (реализовано на Python). Я собираюсь представить только что упомянул неправильное решение Python, который является более доступным для всех:
Решение заключается в следующем:
def DeleteAt(self, position):
"""delete given position"""
self.heap[position] = self.heap[-1]
del(self.heap[-1])
self.TrickleDown(position)
Теперь предположим, что мы имеем следующую мин-макс кучу:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 55 65 37 31
Насколько я проверял, это допустимая куча min-max. Теперь предположим, что мы хотим удалить элемент 55, который в массиве с 0 будет найден в индексе 9 (если бы я правильно подсчитал).
Что решение выше будет сделать, это просто поставить последний элемент в массиве, в данном случае 31, и поставить его в положение 9:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 31 65 37 55
было бы удалить последний элемент массива (который сейчас 55), и в результате мин-макс кучи будет выглядеть следующим образом:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 31 65 37
и, наконец, было бы «просачивания» от position
(т.е. там, где теперь у нас есть номер 31).
«tricle-вниз» будет проверять, если мы в даже (или мин) или нечетное (или макс) уровень: мы в нечетного уровня (3), так что «струйки -down "будет называться" trickle-down-max "начиная с 31, но с 31 ребенка нет, он останавливается (проверьте исходную бумагу выше, если вы не знаете, о чем я говорю).
Но если вы замечаете, что оставляет структуру данных в состоянии, которое не больше мин-макс кучи, потому что 54, что на даже уровне и, следовательно, должен быть меньше, чем его потомков, больше, чем 31, одного из его потомков.
Это заставило меня думать, что мы не могли просто смотреть на детей узла в position
, но нам нужно было проверить с этого position
вверх, что, возможно, нам нужно использовать «просачивания вверх» слишком.
В следующей аргументации, пусть x
будет элементом в position
после удаления элемента, который мы хотели удалить, и до того, как будут выполнены какие-либо операции с исправлениями. Пусть p
будет его родителем (если есть).
Идея моего алгоритма действительно, что один, а более конкретно основывается на том, что:
Если x
находится на нечетном уровне (как в приведенном выше примере), и мы обмениваемся он со своим родителем p
, который находится на ровном уровне, что не нарушит никаких правил/инвариантов кучи min-max из новой позиции x
вниз.
То же рассуждение (я думаю) может быть сделано, если ситуация будет обратная, т.е. x
был первоначально в четной позиции, и это будет больше, чем его родитель.
Теперь, если вы заметили, единственное, что может понадобиться исправить, это то, что если x
был обмен со своим родителем, и теперь он находится в четном (и, соответственно, нечетном) положении, нам может потребоваться проверить, и соответственно больше), чем узел на предыдущем четном (и, соответственно, нечетном) уровне.
Это, конечно, не похоже, чтобы быть все решения для меня, и, конечно, я также хотел бы проверить, если предыдущий родительский x
, т.е. p
, находится в правильном положении.
Если p
, после обмена с x
, находится на нечетной (и, соответственно, даже) уровня, это означает, что она может быть меньше (и, соответственно, больше), чем любой из его потомков, потому что это было ранее в четный (и, соответственно, нечетный) уровень. Итак, я думал, что нам нужно «просачиваться» здесь.
Что касается факта, если p
находится в правильном положении относительно своих предков, я думаю, что рассуждения были бы похожими на приведенные выше (но я не уверен на 100%).
Сведя вместе, я придумал решение:
function DELETE(H, i):
// H is the min-max heap array
// i is the index of the node we want to delete
// I assume, for simplicity,
// it's not out of the bounds of the array
if i is the last index of H:
remove and return H[i]
else:
l = get_last_index_of(H)
swap(H, i, l)
d = delete(H, l)
// d is the element we wanted to remove initially
// and was initially at position i
// So, at index i we now have what was the last element of H
push_up(H, i)
push_down(H, i)
return d
Это похоже на работу в соответствии с реализацией минимакса кучи, что я сделал, и что вы можете найти here.
Следует также отметить, что решение запустить в O (журнал п) время, потому что мы просто вызов «пуш-ап» и «толчок вниз» которые идут в таком порядке.
Прежде всего, спасибо за отзыв. Хорошо, но вы говорите, что нам нужно «пузыриться» ** или _ ** «пузырь». Это не эксклюзив или, верно? Мое единственное сомнение заключается в том, что эти «пузырьковые» и «пузырьковые» могут выполняться в части кучи min-max независимо от других частей ... – nbro
@Nuncameesquecideti: когда вы перемещаете последний элемент в его новая позиция, есть три возможности: 1) она находится в правильном месте; 2) он меньше, чем его родитель, поэтому он должен двигаться вверх; 3) он больше, чем его дети, поэтому он должен двигаться вниз. Как и в двоичной куче, это движение может быть выполнено независимо, потому что действия, которые влияют на одно поддерево (узел и все его предки и иждивенцы), не влияют на другие поддеревья. –
Это означает, что операция «заменить» может быть реализована с помощью операции «удалить» (или аналогичным образом), правильно? – nbro