views:

999

answers:

4

Does Java have an easy way to reevaluate a heap once the priority of an object in a PriorityQueue has changed? I can't find any sign of it in Javadoc, but there has to be a way to do it somehow, right? I'm currently removing the object then re-adding it but that's obviously slower than running update on the heap.

A: 

Depending on the implementation of the data structure, there may not be a faster way. Most PQ/heap algorithms do not provide an update function. The Java implementation may not be any different. Notice that though a remove/insert makes the code slower, it is unlikely to result in code with a different runtime complexity.

Edit: have a look at this thread: http://stackoverflow.com/questions/450180/a-priority-queue-which-allows-efficient-priority-update

Stephan202
+5  A: 

You might need to implement such a heap yourself. You need to have some handle to the position of the item in the heap, and some methods to push the item up or down when its priority has changed.

Some years ago I wrote such a heap as part of a school work. Pushing an item up or down is an O(log N) operation. I release the following code as public domain, so you may use it in any way you please. (You might want to improve this class so that instead of the abstract isGreaterOrEqual method the sort order would rely on Java's Comparator and Comparable interfaces, and also would make the class use generics.)

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());
        }
    }
}
Esko Luontola
+2  A: 

The standard interfaces don't provide an update capability. You have use a custom type that implements this.

And you're right; although the big-O complexity of algorithms that use a heap doesn't change when you remove and replace the top of the heap, their actual run time can nearly double. I'd like to see better built-in support for a peek() and update() style of heap usage.

erickson
+1 for update capabilities. And I also would like to have the standard java Queue or Dequeue have a better implementation for high data volumes. It's really easy to home-cook an implementation that is 30% faster.
Varkhan
+1  A: 

PriorityQueue has the heapify method which re-sorts the entire heap, the fixUp method, which promotes an element of higher priority up the heap, and the fixDown method, which pushes an element of lower priority down the heap. Unfortunately, all of these methods are private, so you can't use them.

I'd consider using the Observer pattern so that a contained element can tell the Queue that its priority has changed, and the Queue can then do something like fixUp or fixDown depending on if the priority increased or decreased respectively.

Adam Jaskiewicz