私はPriorityQueue
自分のプログラムにを実装しています。そのために私も実装しcompareTo()
ました。compareTo()
演じるときに呼ばれているのですがadd()
、これは期待通りです。でも、演奏するときにも呼ばれますpoll()
。poll()の機能は頭を取り除く
ことだと思いました。なぜcompareTo()を呼び出す必要があるのですか?
2 に答える
優先キューの実装方法は、多くの場合、ヒープを使用して行われます。の一部はpoll()ing
、要素を比較するためにヒープを必要とするヒープを再構築する必要があります...したがってcompareTo()
。ただし、これは単なる推測です(つまり、主張を検証するためにソースコードを掘り下げていません)。
興味がある場合は、ヒープを使用して優先度付きキューを実装する方法を簡単に検索できます。http: //pages.cs.wisc.edu/~vernon/cs367/notes/11.PRIORITY-Q.html#imp
実際、楽しみのために、これが厳密ではない方法でどのように機能するかを説明します。ヒープは、ヒーププロパティを満たすツリーです。親は常に子以下(最小ヒープ)であるか、親は常に少なくとも子と同じ大きさ(最大ヒープ)です。PriorityQueue
ミンヒープなのでpoll()
ルートを削除します(これを理解していることを確認してください)。しかし、ルートを削除すると、ツリーはどうなりますか?それはもはやツリーではありません...したがって、これを修正する方法は、ツリーのルートをリーフノードに移動し(ツリーを破壊したり、ヒーププロパティを無効にしたりせずにプルできる)、他のノードを根。しかし、どのノードをルートに入れますか?直感的には、ルートの左または右の子を配置すると思うかもしれません(これらは「元のルートとほぼ同じくらい小さい」)。それは可能ですが、その子をルートとするサブツリーを修正する必要があります(コードは醜いです)。代わりに、(概念的には)同じことを行いますが、コードをより良くするために少し異なる方法で行います。特に、彼らはリーフノードを摘み取り、それをルートに貼り付けます(通常、ルートとリーフノードを交換して両方のステップを同時に実行します)。しかしヒーププロパティは必ずしも満たされていません(ルートにスタックしたリーフノードは非常に大きくなる可能性があります!)。これを修正するには、正しい場所に到達するまで新しいルートを「バブルダウン」します。具体的には、新しいルートを左右の子と比較し、ヒーププロパティが満たされるまで(親が子の少なくとも1つよりも大きい場合)スワッピングを続けます。このスワッピングは確かに有効なヒープにつながることに注意してください(これを証明することはできますが、直感的です)。
すべてがJavaDocにあります(私の強調):
優先度ヒープに基づく無制限の優先度キュー。
そして、あなたのソースコードにはpoll()
次のものがあります。
public E poll() {
//...
if (s != 0)
siftDown(0, x);
return result;
}
どこsiftDown()
にありますか:
/**
* Inserts item x at position k, maintaining heap invariant by
* demoting x down the tree repeatedly until it is less than or
* equal to its children or is a leaf.
* [...]
*/
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
JavaDocのコメントsiftDown()
は非常に重要です。注意深く読んでください。基本的に、の厄介な実装でPriorityQueue
はヒープを使用します。ヒープは、ポーリングによって変更するたびに再構築する必要があります。
なぜあなたはこれに悩まされているのですか?compareTo()
のように、軽量でべき等で副作用のない方法である必要がありますequals()
。制限を加えるべきではありません。