Javaは、PriorityQueue内のオブジェクトの優先度が変更された後、ヒープを再評価する簡単な方法がありますか?Javadoc
にその兆候が見つかりませんが、そこにあります。どういうわけかそれを行う方法である必要がありますよね?私は現在オブジェクトを削除してから再度追加していますが、ヒープで更新を実行するよりも明らかに遅いです。
このようなヒープを自分で実装する必要があるかもしれません。ヒープ内のアイテムの位置を処理する必要があります。また、優先度が変更されたときにアイテムを上下にプッシュする方法もいくつか必要です。
数年前、私は学校の仕事の一環としてそのようなヒープを書きました。アイテムを上下に押すことは、O(log N)操作です。私は次のコードをパブリックドメインとしてリリースしているので、あなたはそれを好きなように使うことができます。 (このクラスを改善して、抽象isGreaterOrEqualメソッドの代わりに、ソート順がJavaのComparatorおよびComparableインターフェースに依存し、クラスがジェネリックを使用するようにすることもできます。)
import Java.util.*;
public abstract class Heap {
private List heap;
public Heap() {
heap = new ArrayList();
}
public void Push(Object obj) {
heap.add(obj);
pushUp(heap.size()-1);
}
public Object pop() {
if (heap.size() > 0) {
swap(0, heap.size()-1);
Object result = heap.remove(heap.size()-1);
pushDown(0);
return result;
} else {
return null;
}
}
public Object getFirst() {
return heap.get(0);
}
public Object get(int index) {
return heap.get(index);
}
public int size() {
return heap.size();
}
protected abstract boolean isGreaterOrEqual(int first, int last);
protected int parent(int i) {
return (i - 1) / 2;
}
protected int left(int i) {
return 2 * i + 1;
}
protected int right(int i) {
return 2 * i + 2;
}
protected void swap(int i, int j) {
Object tmp = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, tmp);
}
public void pushDown(int i) {
int left = left(i);
int right = right(i);
int largest = i;
if (left < heap.size() && !isGreaterOrEqual(largest, left)) {
largest = left;
}
if (right < heap.size() && !isGreaterOrEqual(largest, right)) {
largest = right;
}
if (largest != i) {
swap(largest, i);
pushDown(largest);
}
}
public void pushUp(int i) {
while (i > 0 && !isGreaterOrEqual(parent(i), i)) {
swap(parent(i), i);
i = parent(i);
}
}
public String toString() {
StringBuffer s = new StringBuffer("Heap:\n");
int rowStart = 0;
int rowSize = 1;
for (int i = 0; i < heap.size(); i++) {
if (i == rowStart+rowSize) {
s.append('\n');
rowStart = i;
rowSize *= 2;
}
s.append(get(i));
s.append(" ");
}
return s.toString();
}
public static void main(String[] args){
Heap h = new Heap() {
protected boolean isGreaterOrEqual(int first, int last) {
return ((Integer)get(first)).intValue() >= ((Integer)get(last)).intValue();
}
};
for (int i = 0; i < 100; i++) {
h.Push(new Integer((int)(100 * Math.random())));
}
System.out.println(h+"\n");
while (h.size() > 0) {
System.out.println(h.pop());
}
}
}
PriorityQueueには、ヒープ全体を再ソートするheapify
メソッド、ヒープの上位に優先度の高い要素を昇格させるfixUp
メソッド、およびプッシュするfixDown
メソッドがあります。ヒープの下の優先度の低い要素。残念ながら、これらのメソッドはすべてプライベートであるため、使用できません。
含まれている要素がキューに優先度が変更されたことを通知できるようにオブザーバーパターンを使用することを検討します。キューは、優先度が上がったかどうかに応じて、fixUp
またはfixDown
のようなことを実行できます。またはそれぞれ減少しました。
そのとおり。 PriorityQueue
of Javaは優先度を更新する方法を提供しておらず、Map
のようにオブジェクトをキーとして保存しないため、削除には直線的な時間がかかるようです。実際、同じオブジェクトを複数回受け入れます。
また、PQオファリングの更新操作を行いたいと思いました。ジェネリックを使用したサンプルコードは次のとおりです。 Comparableである任意のクラスを使用できます。
class PriorityQueue<E extends Comparable<E>> {
List<E> heap = new ArrayList<E>();
Map<E, Integer> map = new HashMap<E, Integer>();
void insert(E e) {
heap.add(e);
map.put(e, heap.size() - 1);
bubbleUp(heap.size() - 1);
}
E deleteMax() {
if(heap.size() == 0)
return null;
E result = heap.remove(0);
map.remove(result);
heapify(0);
return result;
}
E getMin() {
if(heap.size() == 0)
return null;
return heap.get(0);
}
void update(E oldObject, E newObject) {
int index = map.get(oldObject);
heap.set(index, newObject);
bubbleUp(index);
}
private void bubbleUp(int cur) {
while(cur > 0 && heap.get(parent(cur)).compareTo(heap.get(cur)) < 0) {
swap(cur, parent(cur));
cur = parent(cur);
}
}
private void swap(int i, int j) {
map.put(heap.get(i), map.get(heap.get(j)));
map.put(heap.get(j), map.get(heap.get(i)));
E temp = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, temp);
}
private void heapify(int index) {
if(left(index) >= heap.size())
return;
int bigIndex = index;
if(heap.get(bigIndex).compareTo(heap.get(left(index))) < 0)
bigIndex = left(index);
if(right(index) < heap.size() && heap.get(bigIndex).compareTo(heap.get(right(index))) < 0)
bigIndex = right(index);
if(bigIndex != index) {
swap(bigIndex, index);
heapify(bigIndex);
}
}
private int parent(int i) {
return (i - 1) / 2;
}
private int left(int i) {
return 2*i + 1;
}
private int right(int i) {
return 2*i + 2;
}
}
ここで更新中は、(実装の)優先度を上げるだけで、MaxHeapを使用しているため、bubbleUpを実行しています。要件に基づいてヒープ化する必要がある場合があります。
標準インターフェースは更新機能を提供しません。これを実装するカスタムタイプを使用しています。
そして、あなたは正しいです。ヒープを使用するアルゴリズムのbig-Oの複雑さは、ヒープの上部を削除して交換しても変わりませんが、実際の実行時間はほぼ2倍になる可能性があります。 peek()
およびupdate()
スタイルのヒープ使用法に対するより優れた組み込みサポートが必要です。
データ構造の実装によっては、より高速な方法がない場合があります。ほとんどのPQ /ヒープアルゴリズムは更新機能を提供しません。 Javaの実装に違いはないかもしれません。削除/挿入によってコードが遅くなりますが、実行時の複雑さが異なるコードになる可能性は低いことに注意してください。
編集:このスレッドを見てください: 効率的な優先度更新を可能にする優先度キュー?
残念ながら、JDKの優先キューは更新を提供しません。 RobertSedgewickとKevinWayneは、プリンストンでのアルゴリズムコースでよく知られており、 Algorithms とも書いています。
この優れた本の中で、更新可能な priority queues ( IndexMinPQ.Java など)を含む、データ構造の独自の実装を提供しています。
GPLv3の下でライセンスされています。