1

Примечание: этот вопрос был вызван тем фактом, что я нигде в Интернете не нашел (правильное) решение «как удалить любой узел из кучи min-max». Решение, которое я представляю в качестве ответа, может быть правильным, или не, так как я не представляю никаких доказательств его правильности, а просто объяснений. Итак, цель этого вопроса состоит в том, чтобы дать экспертов в области алгоритмов и структур данных, чтобы прокомментировать это решение и, в случае необходимости, предоставить один конкретный и правильный. Я делаю это для всего сообщества, а не только для себя.Как выполнить операцию общего удаления в куче min-max?

Как выполнить операцию общего удаления в куче min-max?

Куча min-max может быть полезна для реализации очереди с двойным приоритетом из-за ее постоянного времени find-min и find-max операций. Мы также можем извлечь минимальный и максимальный элементы в куче min-max в O (log n) время. Иногда, однако, мы можем также удалить любой узел в минимакса кучу, и это может быть сделано в O (журнал п), according to the (what I think is) original paper presenting min-max heaps:

...

Структура также может быть обобщена для поддержки операции Find(k) (определите наименьшее значение в структуре kth) в постоянное время и операцию Delete(k) (удалите k-е наименьшее значение в структуре) в логарифмическом времени для любого фиксированного значения (или набор значений) k.

...

Не уверен, хотя, если они имели в виду именно к общему «удалить все» узел ...

ответ

0

Что привело меня к разработке этого решения (который я не 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 будет его родителем (если есть).

Идея моего алгоритма действительно, что один, а более конкретно основывается на том, что:

  1. Если 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 (журнал п) время, потому что мы просто вызов «пуш-ап» и «толчок вниз» которые идут в таком порядке.

2

Я не считаю себя «экспертом» в областях алгоритмов и структур данных, но у меня есть подробное понимание двоичных куч, включая кучу min-max. См., Например, мою серию блога о двоичных кучах, начиная с http://blog.mischel.com/2013/09/29/a-better-way-to-do-it-the-heap/. У меня есть реализация min-max, о которой я расскажу в какой-то момент.

Ваше решение проблемы правильно: вам действительно нужно пузыриться или просачиваться вниз, чтобы повторно настроить кучу, когда вы удаляете произвольный узел.

Удаление произвольного узла в куче min-max принципиально не отличается от той же операции в макс-куче или в мини-куче. Рассмотрим, например, удаление произвольного узла в мини-куче. Начните с этой мин кучей:

 0 
    4  1 
5 6 2 3 

Теперь, если вы удалите узел 5 у вас есть:

 0 
    4  1 
    6 2 3 

Берет последний узел в куче, 3, и поместить его в том месте, где- был:

 0 
    4  1 
3 6 2 

в этом случае вам не придется просеивать вниз, потому что это уже лист, но это из места, потому что это меньше, чем его родитель. Вы должны пузыриться, чтобы получить:

 0 
    3  1 
4 6 2 

Те же правила применяются для кучи min-max. Вы заменяете элемент, который вы удаляете последним элементом из кучи, и уменьшаете количество. Затем вы должны проверить, нужно ли его пузыряться или просеять вниз. Единственная сложная часть состоит в том, что логика отличается в зависимости от того, находится ли элемент на минимальном уровне или максимальном уровне.

В вашем примере куча, полученная в результате первой операции (замена 55 на 31), недействительна, так как 31 меньше 54. Таким образом, вы должны пузыриться в куче.

Еще одна вещь: удаление произвольного узла действительно является журналом (n).Однако нахождение удаляемый узел является операцией O (n), если у вас нет другой структуры данных, отслеживающей, где узлы находятся в куче. Таким образом, в общем случае удаление произвольного узла считается O (n).

+0

Прежде всего, спасибо за отзыв. Хорошо, но вы говорите, что нам нужно «пузыриться» ** или _ ** «пузырь». Это не эксклюзив или, верно? Мое единственное сомнение заключается в том, что эти «пузырьковые» и «пузырьковые» могут выполняться в части кучи min-max независимо от других частей ... – nbro

+0

@Nuncameesquecideti: когда вы перемещаете последний элемент в его новая позиция, есть три возможности: 1) она находится в правильном месте; 2) он меньше, чем его родитель, поэтому он должен двигаться вверх; 3) он больше, чем его дети, поэтому он должен двигаться вниз. Как и в двоичной куче, это движение может быть выполнено независимо, потому что действия, которые влияют на одно поддерево (узел и все его предки и иждивенцы), не влияют на другие поддеревья. –

+0

Это означает, что операция «заменить» может быть реализована с помощью операции «удалить» (или аналогичным образом), правильно? – nbro

Смежные вопросы