如何使用二进制堆来实现优先级队列?

时间:2013-11-16 19:13:32

标签: algorithm data-structures heap priority-queue binary-heap

尽管事实上我已经获得了计算机科学学士学位(这在大学里有所涉及),但我从来没有能够理解二进制堆和优先级队列之间的关系。它只是...没有点击。我完全理解二进制堆是什么,我知道如何在数组中实现一个。我也知道优先队列是什么。但是他们两个如何结合在一起呢?

快速谷歌查询显示了许多文章,如this one。哪种解释,但我还有更多问题:

  1. 优先级队列需要确保如果添加了两个具有相同优先级的项目,那么它们将按照添加它们的顺序删除。二进制堆如何确保这一点? (事实上​​,如果我没有弄错的话,那么所有项目具有相同优先级的边界情况会产生违反此规则的堆。)
  2. 当您从堆中删除根,然后放入最后一个元素代替根,然后需要将其与其中一个子项交换时会发生什么 - 但两个子节点具有相同的值。你如何选择与哪一个交换? (记住相同优先级项目的FIFO规则)
  3. 我在这里缺少什么?

2 个答案:

答案 0 :(得分:8)

优先级队列是一种抽象数据类型,如“stack”或“associative array”。可以有许多不同的方法来实现优先级队列 - 您可以使用二进制堆,二项式堆,Fibonacci堆,配对堆等。

我不认为优先级队列有任何内在要求是“稳定的”,并且要求具有相同优先级的元素以与添加它们相同的相对顺序出列,这与没有相同的方式相同排序算法稳定的内在要求(虽然很多)。这是一个不错的财产,但通常不保证。这就是为什么,例如,标准的堆不是一个稳定的类型。

幸运的是,调整二进制堆以保持稳定并不太难。保持与二进制堆相关联的计数器。每当向堆中添加元素时,使用计数器的当前值标记它并递增计数器。在执行堆操作时,使用计数器作为仲裁器,以确定如果两个值比较相等则哪个值更小。从本质上讲,这会将比较更改为首先根据实际值进行词典编制比较,然后再根据插入顺序进行比较。

希望这有帮助!

答案 1 :(得分:0)

简单的Java代码来说明优先级队列

package heap;

import java.util.Comparator;

/**
 * Generic implementation of priority queue based on heap with add and pop
 * Reverse is min heap
 *  Average Worst case
 Space  O(n)    O(n)
 Search O(n)    O(n)
 Insert O(1)    O(log n)
 Delete O(log n)    O(log n)
 * Created by  on 9/7/14.
 */
public class BinaryHeap<T extends Comparable> {
    private final Comparable[] pq;
    private int size = 0;

    private final Comparator<Comparable> comparator;

    private final static Comparator<Comparable> comparatorMax = new Comparator<Comparable>() {
        @Override
        public int compare(Comparable o1, Comparable o2) {
            return o1.compareTo(o2);
        }
    };


    public BinaryHeap(int size) {
        this(size,false);
    }

    public BinaryHeap(int size, boolean reverse) {
        pq = new Comparable[size];

        if(reverse)
            comparator = comparatorMax.reversed();
        else
            comparator = comparatorMax;
    }


    public void add(T entry) throws Exception {
        if(size == pq.length)
            throw new Exception("Heap Overflow Exception: "+ entry);
        pq[size] = entry;
        size++;
        maxHeapify(pq, getNewPos(size - 1), true);
        print();
    }

    public Comparable pop() throws Exception {
        if(size == 0)
            return null;
        size--;
        swap(pq,0,size);
        Comparable entry = pq[size];
        pq[size] = null;
        maxHeapify(pq, 0, false);
        System.out.println("Popped: "+ entry);
        print();
        return entry;
    }

    public Comparable find(T entry) {
        for(int i=0; i < size; i++)
            if(comparator.compare(entry,pq[i]) == 0)
                return entry;

        return null;
    }

    private void maxHeapify(Comparable[] entries, int pos, boolean swimUp) throws Exception {
        int leftPos = 2 * pos + 1;
        int rightPos = 2 * pos + 2;

        Comparable parent = entries[pos];
        Comparable left = null;
        if(leftPos < size)
            left = entries[leftPos];

        Comparable right = null;

        if(rightPos < size)
            right = entries[rightPos];

        if(left == null && right == null)
            return; //For swim Down case

        if (parent == null)
            throw new Exception("Entry not found Exception: " + pos);

        //Find the largest of left and right for swaps
        int largest = pos;
        if (left != null && comparator.compare(parent,left) < 0) {
            largest = leftPos;
        }

        if (right != null && comparator.compare(parent,right) < 0) {
            if(largest == pos)
                largest = rightPos;
            else if(comparator.compare(right,entries[largest]) > 0) {
                largest = rightPos;
            }

        }

        if (largest != pos) {
            swap(entries, largest, pos);
            if (swimUp && pos == 0)
                return;

            maxHeapify(entries, swimUp ? getNewPos(pos) : largest, swimUp);
        }
    }


    private void swap(Comparable[] entries, int pos1, int pos2) {
        Comparable temp = entries[pos2];
        entries[pos2] = entries[pos1];
        entries[pos1] = temp;
    }

    private int getNewPos(int pos) {
        int newPos = pos / 2;//Takes the floor automatically because of int
        if (pos > 0 && pos % 2 == 0)
            newPos = (pos / 2) - 1;

        return newPos;
    }

    public void print() {
        System.out.print("[");
        int i=0;
        for(;i < size-1; i++)
            System.out.print(pq[i] + ",");

        System.out.print(pq[i]+"]");
        System.out.println();
    }

    public static void main(String[] args) {
        BinaryHeap<Integer> pq = new BinaryHeap<Integer>(100);
        try {
            pq.add(5);

            pq.add(3);

            pq.add(9);

            pq.add(16);
            pq.add(2);
            pq.add(3);
            pq.add(19);
            pq.add(500);
            pq.add(90);
            pq.add(1);
            pq.add(91);
            pq.add(600);
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();
            pq.pop();


        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}