web-dev-qa-db-ja.com

ヒープデータ構造で削除する方法は?

最大ヒープからルートノードを削除する方法を理解していますが、目的のノードが削除されるまでルートを繰り返し削除および置換するために中間からノードを削除する手順はありますか?

  1. O(log n)はこの手順の最適な複雑度ですか?

  2. 特定のノードを削除するには他のノードを削除する必要があるため、これは大きなOの複雑さに影響しますか?

44
tesfa koli

実際、問題なくヒープの中央からアイテムを削除できます。

アイデアは、ヒープ内の最後のアイテムを取得し、現在の位置(つまり、削除したアイテムを保持していた位置)から始めて、新しいアイテムが古いアイテムの親よりも大きい場合、それをふるいにかけます。親より大きくない場合は、ふるいにかけます。

これが最大ヒープの手順です。もちろん、最小ヒープの場合は、大小のケースを逆にします。

Findingヒープ内の項目はO(n)操作ですが、すでにどこにあるかがわかっている場合ヒープ、削除するのはO(log n)です。

数年前、DevSourceのヒープベースの優先度キューを公開しました。 C#での優先度キューの実装 を参照してください。これには、説明したとおりのことを行うRemoveAtメソッドがあります。

完全なソースは http://www.mischel.com/pubs/priqueue.Zip にあります

更新

削除されたノードを置き換えるためにヒープ内の最後のノードを移動した後、上に移動できるかどうかを尋ねる人がいます。このヒープを検討してください。

        1
    6       2
  7   8   3

値7のノードを削除すると、値3に置き換えられます。

        1
    6       2
  3   8

有効なヒープを作成するには、これを上に移動する必要があります。

        1
    3       2
  6   8

ここで重要なのは、置換するアイテムがヒープ内の最後のアイテムとは異なるサブツリーにある場合、置換ノードが置換ノードの親よりも小さくなる可能性があることです。

83
Jim Mischel

ヒープから任意の要素を削除する際の問題は、それが見つからないことです。

ヒープでは、任意の要素を探すことはO(n)です。したがって、[値で指定されている場合]要素を削除することもO(n)です。

データ構造から任意の要素を削除することが重要である場合、ヒープはおそらく最良の選択ではありません。代わりに balanced BST または skipなどの完全なソートされたデータ構造を検討する必要があります。リスト

要素が参照で指定されている場合、O(logn)でそれを最後のリーフに単に「置き換える」ことで削除できます[ヒープは完全なバイナリツリーとして実装されているため、最後の葉、そしてあなたはそれがどこにあるかを正確に知っています]、これらの要素を削除し、関連するサブヒープを再ヒープ化します。

16
amit

最大ヒープがある場合、他よりも大きい値を割り当てることでこれを実装できます(例:int.MaxValueまたはinfを使用している言語で)削除するアイテムに追加してから、再ヒープ化すると、新しいルートになります。次に、ルートノードを定期的に削除します。

これにより別の再ヒープ化が発生しますが、2回行うことを回避する明白な方法は見当たりません。これは、ノードを頻繁に途中からプルする必要がある場合、ヒープがユースケースに適切でない可能性があることを示唆しています。

(最小ヒープの場合は、明らかにint.MinValueまたは-infまたは何でも)

2
rejj

あなたが達成したいことは典型的なヒープ操作ではなく、メソッドとして「中間要素の削除」を導入したら、他のバイナリツリー(たとえば、赤黒またはAVLツリー)がより良い選択だと思われます。いくつかの言語で実装された赤黒ツリーがあります(たとえば、マップし、c ++で設定します)。

それ以外の場合、中間要素を削除する方法はrejjの答えで提案されているとおりです:大きな値(最大ヒープの場合)または小さな値(最小ヒープの場合)を要素に割り当て、ルートになるまでそれをふるいにかけて削除します。

このアプローチでは、中間要素の削除のO(log(n))複雑さは維持されますが、提案するものはそうです。複雑さO(n * log(n))があり、したがってあまり良くありません。

1