私がこのソリューションを開発することになったのは (100% 正しいとは言えません)、実際に min-max ヒープ内の任意のノードを削除するソリューションを見つけたという事実ですが、それは間違っています。
間違った解決策は、こちら( C++ で実装) とこちら(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
私が確認した限り、これは有効な最小最大ヒープです。ここで、エレメント 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という数字がある場所)。
「トリクルダウン」は、偶数(または最小) または奇数(または最大) レベルにあるかどうかをチェックします。奇数レベル (3) にあるため、「トリクルダウン」は「トリクルダウン-」を呼び出します。 max" を 31 から開始しますが、31 には子がないため停止します (私が何を話しているのかわからない場合は、上記の元の論文を確認してください)。
しかし、最小-最大ヒープではない状態でデータ構造を残していることが観察された場合、偶数レベルであり、したがってその子孫よりも小さいはずの 54 が、その子孫の 1 つである 31 よりも大きいためです。
これにより、 のノードの子だけを調べるだけではなくposition
、その上からも確認する必要があるのではないかと考えposition
ました。「トリクルアップ」も使用する必要があるのではないでしょうか。
次の推論では、削除したい要素を削除した後、修正操作が実行される前の要素としますx
。position
その親にしましょうp
(存在する場合)。
私のアルゴリズムのアイデアはまさにそれであり、より具体的には次の事実に基づいています。
ifx
が奇数レベル (上記の例のように) にあり、それを偶数レベルにあるその parent と交換します。これは、 newの位置p
から下の最小最大ヒープのルール/不変条件を破ることはありません。x
.
x
もちろん、これは私にとって完全な解決策ではないように思われました。もちろん、 の前の親、つまりp
が正しい位置にあるかどうかも確認したかったのです。
これをまとめると、私は解決策を思いつきました:
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
これは、私が作成した最小最大ヒープの実装に従って動作するようで、ここで見つけることができます。
また、ソリューションはO(log 2 n)時間で実行されることに注意してください。これは、この順序で実行される「プッシュアップ」と「プッシュダウン」を呼び出しているだけだからです。