【问题标题】:create PriorityQueue in O(n ) with custom comparator使用自定义比较器在 O(n) 中创建 PriorityQueue
【发布时间】:2018-04-15 07:56:12
【问题描述】:

我正在尝试使用带有自定义比较器的 Priorityqueue 实现 MST,但是在 O(n) 时间内使用它构建最小堆时遇到了问题。问题是 Priorityqueue 只有一个构造函数允许在 O(n) 中创建 PriorityQueue,但它不接受任何比较器作为参数。我希望它使用我的自定义比较器。这个问题有解决方法吗? PriorityQueue.addAll() 将失去使用 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;
            }
        }
    });

【问题讨论】:

  • 请展示你的作品
  • @Noixes 已更新。
  • PriorityQueue.addAll() ... 是 O(nlogn) 方法。你有这方面的证据吗?
  • 实际上,我对这篇文章的公认答案感到困惑。 stackoverflow.com/questions/34693414/…
  • @jrook: PriorityQueueAbstractQueue 继承 addAll。它不会覆盖实现。 AbstractQueue.addAll 的文档说,此实现迭代指定的集合,并将迭代器返回的每个元素依次添加到此队列中。 那将是 O(n log n)。跨度>

标签: java priority-queue min-heap


【解决方案1】:

如果你没有在其他地方对你的列表进行最小堆排序,你将无法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&lt;edge&gt;。然后你就可以PriorityQueue&lt;edge&gt; pr = new PriorityQueue(ar);

如果您无法控制edge implements Comparable&lt;edge&gt;,那么您可以编写一个容器类:

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 在某种程度上,他做到了,因为他不能在同一个构造函数中添加集合和比较器......并且使用addAll 筛选而不是向下筛选......
    • @corsiKa,你是对的。但是通过为集合中包含的对象实现compareTo() 不会解决这个问题吗?
    • 是的,这是一种可能性——OP 可能控制也可能不控制——尽管他可以编写一个容器类?
    • @jrook 是否适用于 PriorityQueue 进行自然排序?
    • 在您说的第一段中,每个heapify() 都需要log(n) 时间。 我理解这一点以及以下讨论意味着您说的是 O(1)插入项目的参数。我认为我的困惑来自您使用名称 heapify() 来表示两个不同的事物(构建堆的函数和构建堆函数调用的函数)。将该句子更改为每次调用siftDown() 都需要log(n) 时间。 很多人使用heapify() 代替siftDown() 并没有帮助。我希望我们都能就术语达成一致。
    猜你喜欢
    • 2011-02-14
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-14
    • 2017-11-02
    相关资源
    最近更新 更多