使用自定义比较器在O(n)中创建PriorityQueue

时间:2017-11-02 17:29:11

标签: java priority-queue min-heap

我试图用自定义比较器实现带有Priorityqueue的MST,但是我在O(n)时间内用它构建min-heap时遇到了问题。问题是,Priorityqueue的一个构造函数只允许在O(n)中创建PriorityQueue,但它不会将任何比较器作为参数。我希望它使用我的自定义比较器。这个问题有解决方法吗? PriorityQueue.addAll()将失去使用Min-heap作为MST的目的,因为它是O(nlogn)方法。这是我的代码。

ArrayList <edge>ar=new ArrayList<>(); 
   for(int i=0;i<e;i++)
   {
       int u=ss.nextInt();
       int v=ss.nextInt();
       int w=ss.nextInt();
       ar.add(new edge(u,v,w));
   }
   PriorityQueue <edge>pr=new PriorityQueue<edge>(ar);

我要使用的比较器: -

PriorityQueue <edge>ar=new PriorityQueue(11,new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            edge n1=(edge) o1;
            edge n2=(edge) o2;
            if(n1.w<n2.w)
            {
                return -1;
            }
            else if(n1.w==n2.w)
            {
                if((n1.u+n1.v+n1.w)<=(n2.u+n2.v+n2.w))
                {
                    return -1;
                }   
                else
                {
                    return 1;
                }
            }
            else
            {
                return 1;
            }
        }
    });

2 个答案:

答案 0 :(得分:3)

如果你没有在其他地方对你的列表进行最小堆排序,你将无法new PriorityQueue(...)任何东西,并以某种方式避免创建堆的命中。数学here表示平均情况为O(n),但它不仅仅是迭代。

PriorityQueue<edge> pr = new PriorityQueue<edge>(ar, comp) {
    PriorityQueue(List<edge> ar, Comparator<edge> c) {
        this(c);
        for(int i = 0; i < queue.length; i++) {
            queue[i] = ar.get(i);
        }
        this.size = queue.length;
        heapify(); // O(n), except that heapify is private and thus you can't call it!!!
    }
}

现在我还没有对此进行过测试,它只是在我的头顶,有一些来自PriorityQueue源的指导,但它应该指向正确的方向。

但是有时候你必须支付piper并创建堆顺序,这不仅仅是迭代。但是,由于O(n),它仍然应该在heapify上。

另一个选择是edge实施Comparable<edge>。然后你可以PriorityQueue<edge> pr = new PriorityQueue(ar);

如果您无法控制edge implements Comparable<edge>,那么您可以撰写容器类:

class EdgeContainer implements Comparable<EdgeContainer> {

    private static final Comparator<edge> comp = ; // that comparator above
    private final edge edge;
    EdgeContainer(Edge edge) { this.edge = edge; }
    public int compareTo(EdgeContainer e) { return comp.compare(edge, e.edge); }
    public edge getEdge() { return edge; }
}

List <EdgeContainer>ar=new ArrayList<>(); 
for(int i=0;i<e;i++)
{
   int u=ss.nextInt();
   int v=ss.nextInt();
   int w=ss.nextInt();
   ar.add(new EdgeContainer(new edge(u,v,w)));
}

PriorityQueue<EdgeContainer> qr = new PriorityQueue(ar);

答案 1 :(得分:2)

Java PriorityQueue花费O(n)时间从传递给它的集合中创建优先级队列。 CLSR第6.4章(第3版第157页)给出了数学证明。直观地说,当底层数组使用siftDownsiftUp变为堆时,下一个sift操作循环的元素数量的大小也会减小,从而导致O (n)时间复杂性。

但正如评论中所讨论的那样,正如您在问题中提到的那样,使用addAll()无法实现这种时间复杂性。原因是adAll()继承自AbstractQueue,并且通过将集合中的元素逐个添加到队列中来工作,这可能导致O(nlogn)时间复杂度。

因此,如果O(n)时间复杂度是绝对要求,则除了为集合中包含的对象类实现Comparator接口之外别无选择。 @ corsiKa的回答很好地详述了这种方法。另请注意,即使您将集合直接传递给PriorityQueue,它也会将其转换为基本上是另一个O(n)操作的数组。