web-dev-qa-db-fra.com

PriorityQueue/Mise à jour de tas

Java dispose-t-il d’un moyen facile de réévaluer un segment de mémoire une fois que la priorité d’un objet dans une PriorityQueue a changé? Je ne trouve aucun signe de cela dans Javadoc, mais il doit y avoir un moyen de le faire d'une manière ou d'une autre, non? Je suis en train de supprimer l'objet puis de le rajouter, mais c'est évidemment plus lent que l'exécution de la mise à jour sur le tas.

35
kevmo314

Vous pourriez avoir besoin de mettre en place un tel tas vous-même. Vous devez connaître la position de l'élément dans le tas et connaître les méthodes permettant de pousser l'élément vers le haut ou le bas lorsque sa priorité a changé.

Il y a quelques années, j'ai écrit un tel tas dans le cadre d'un travail scolaire. Pousser un élément vers le haut ou le bas est une opération O (journal N). Je publie le code suivant comme domaine public, vous pouvez donc l'utiliser comme bon vous semble. (Vous voudrez peut-être améliorer cette classe afin qu'au lieu de la méthode abstraite isGreaterOrEqual, l'ordre de tri repose sur les interfaces Comparator et Comparable de Java et fait en sorte que la classe utilise des génériques.)

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());
        }
    }
}
16
Esko Luontola

PriorityQueue a la méthode heapify qui re-trie le tas entier, la méthode fixUp, qui promeut un élément de priorité supérieure dans le tas, et la méthode fixDown, qui pousse un élément de priorité inférieure dans le tas. Malheureusement, toutes ces méthodes sont privées, vous ne pouvez donc pas les utiliser.

J'envisagerais d'utiliser le modèle Observer de sorte qu'un élément contenu puisse indiquer à la file d'attente que sa priorité a changé et que la file d'attente peut alors effectuer une opération telle que fixUp ou fixDown, selon que la priorité augmente ou diminue respectivement.

8
Adam Jaskiewicz

C'est vrai. PriorityQueue of Java n'offre pas de méthode pour mettre à jour la priorité et il semble que la suppression prenne du temps de manière linéaire car elle ne stocke pas les objets sous forme de clés, comme le fait Map. Il accepte en fait le même objet plusieurs fois.

Je voulais aussi faire fonctionner la mise à jour de l'offre PQ. Voici l'exemple de code utilisant des génériques. Toute classe qui est comparable peut être utilisée avec elle.

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;
    }
}

Ici, lors de la mise à jour, je n’augmente que la priorité (pour mon implémentation) et il utilise MaxHeap, donc je fais bubbleUp. Il peut être nécessaire de déterminer les besoins en fonction des besoins.

3
whitehat

Les interfaces standard ne fournissent pas une capacité de mise à jour. Vous avez utilisé un type personnalisé qui implémente cela. 

Et tu as raison Bien que la complexité big-O des algorithmes utilisant un segment de mémoire ne change pas lorsque vous supprimez et remplacez le sommet du segment de mémoire, leur temps d'exécution réel peut presque doubler. Je souhaite un meilleur support intégré pour l'utilisation de tas de style peek() et update().

3
erickson

Selon la mise en œuvre de la structure de données, il peut ne pas y avoir de moyen plus rapide. La plupart des algorithmes PQ/heap ne fournissent pas de fonction de mise à jour. L'implémentation Java peut ne pas être différente. Notez que bien qu'une suppression/insertion ralentisse le code, il est peu probable qu'il en résulte un code avec une complexité d'exécution différente.

Edit : regardez ce fil de discussion: Une file de priorité qui permet une mise à jour efficace de la priorité?

2
Stephan202

Malheureusement, la file d'attente des priorités du JDK ne fournit pas de mises à jour. Robert Sedgewick et Kevin Wayne sont bien connus pour leurs cours d'algorithmes à Princeton, et ils ont également écrit Algorithms .

Dans cet excellent livre, ils fournissent leurs propres implémentations pour les structures de données, y compris les files d'attente modifiables priority , telles que IndexMinPQ.Java

Sous licence GPLv3.

0
Ron Klein