当前位置: 首页 > 知识库问答 >
问题:

Java优先级队列构建堆进程时间复杂性

端木野
2023-03-14

构建堆的时间复杂度为O(n)。问题是Java优先级队列是否可以实现它?让我们举一个例子:

ArrayList<Integer> list=new ArrayList<Integer>(Arrays.asList(5,6,7,0,1,33));
Queue<Integer> queue=new PriorityQueue<Integer>(list); // Seems O(n) if it uses efficient build heap

while(!queue.isEmpty()) {
    System.out.println(queue.poll()); //log(n)
}

...

现在我们可以看到,它将按照自然顺序进行排序,即递增顺序。Java没有一个构造函数既接受列表又接受比较器。如果我想创建Max Heap,则如下所示:

...

ArrayList<Integer> list=new ArrayList<Integer>(Arrays.asList(5,6,7,0,1,33));
Queue<Integer> queue=new PriorityQueue<Integer>(new Comparator<Integer>() {
    @Override
    public int compare(Integer arg0, Integer arg1) {
        return arg1.compareTo(arg0);
    }});
for(Integer val:list) {
    queue.offer(val);
}
// nlog(n) in above for loop. Can't use build heap
while(!queue.isEmpty()) {
    System.out.println(queue.poll());
}

...

如果我想使用PriorityQueue对自定义类进行排序,那么它将永远无法实现构建堆O(n)。或者我们可以吗?

共有1个答案

松茂实
2023-03-14

当我运行以下程序时

System.out.println("Elements    Via constructor      Via offer()");
System.out.println("               Add Then-remove       Add Then-remove");

for(int size: new int[] { 10, 100, 1_000, 10_000, 100_000, 1_000_000 }) {
    int[] comparisons = { 0 };
    class Element implements Comparable<Element> {
        int x;
        Element(int x) { this.x = x; }
        public int compareTo(Element o) {comparisons[0]++;return Integer.compare(x,o.x);}
        @Override public String toString() { return String.valueOf(x); }
    }
    List<Element> samples = IntStream.range(0, size)
        .mapToObj(Element::new)
        .collect(Collectors.toCollection(ArrayList::new));
    Collections.shuffle(samples, ThreadLocalRandom.current());

    System.out.printf("%8d", samples.size());

    PriorityQueue<Element> q1 = new PriorityQueue<>(samples);
    System.out.printf("%10d", comparisons[0]);
    comparisons[0] = 0;
    while(!q1.isEmpty()) q1.remove();
    System.out.printf("%12d", comparisons[0]);

    comparisons[0] = 0;
    PriorityQueue<Element> q2 = new PriorityQueue<>(Comparator.naturalOrder());
    for(Element e: samples) q2.offer(e);
    System.out.printf("%10d", comparisons[0]);
    comparisons[0] = 0;
    while(!q2.isEmpty()) q2.remove();
    System.out.printf("%12d", comparisons[0]);
    System.out.println();
}

我反复得到的结果是

Elements    Via constructor      Via offer()
               Add Then-remove       Add Then-remove
      10        13          27        13          27
     100       169         850       205         850
    1000      1863       14949      2264       14947
   10000     18820      216653     22696      216736
  100000    188046     2831466    228471     2831779
 1000000   1881455    34913307   2281512    34914028

显示通过offer添加元素确实会执行更多比较,但仍然具有O(n)时间复杂性。由于检索元素表现出O(n logn)复杂性,因此它使结构上的差异变得微不足道。

进一步注意,如果没有洗牌,即使用预先排序的数据,两种方法之间根本没有区别。实际上,数据可能介于排序和完全随机之间。

所以对大多数实际目的来说,答案是这并不重要。

如果你真的需要在受控环境中挤出最大的性能,你可以使用黑客:

Comparator<Element> comparator = Comparator.naturalOrder();
PriorityQueue<Element> q = new PriorityQueue<>(
    new PriorityQueue<>(comparator) { // not really recommended
        @Override
        public Object[] toArray() {
            return samples.toArray();
        }
    });

PriorityQueue将从输入PriorityQueue中获取比较器,但是当getClass()时,不会信任toArray()结果中元素的顺序!=PriorityQueue.class,如本例所示,并返回到将任意集合传递给构造函数时使用的相同例程。最终结果是设置比较器和使用任意集合输入初始化的行为。

当然,对于您计划提供给其他人的任何代码,都不建议依赖这些实现细节。但除非将来的版本添加PriorityQueue(集合

但正如本答案的第一部分所示,您可能无论如何都不需要这种破解,因为通过offer添加所有元素也具有合理的性能。

 类似资料:
  • 注意:我知道可以用比较器创建优先级队列,然后重复调用Add。

  • 我有一个优先级队列最大堆,其中每个元素都是一个名为Task的类,如下所示(在Java中实现,但问题与语言无关): 堆显然是按优先级排序的。我要执行的操作是查找优先级最高的项目,递减其 Time 值,并在时间分数更改为 0 时将其从堆中删除。但是,这里有一个问题:允许有多个具有相同优先级的任务。在这种情况下,我比较所有此类任务的ID分数,并对最低的任务进行操作。 此类操作的最坏运行时间是多少?如果每

  • 我不熟悉堆,二进制堆,我试图理解为什么我们需要使用二进制堆实现优先级队列。我还了解到二进制堆的底层数据结构也是一个数组。 所以我的问题是,为什么我们不能使用一个数组,按降序(对于最大堆)或升序(对于最小堆)排序来表示优先级队列?这里我可能错了,但我认为,如果以这种方式实现,findMax、findMin、insert和delete等操作的时间复杂度将几乎保持不变。那么,我们是否可以不使用排序数组来

  • 创建堆需要时间,而插入堆(或优先级队列)需要时间。 取n个输入并将其插入优先级队列,操作的时间复杂度是多少?O(n)或O(n*log(n))。 此外,如果清空整个堆(即n个删除),同样的结果也成立,对吧?

  • 我正在编写一个涉及堆实现的代码,在我的bubbleUp方法中,在我的while循环行中,我似乎遇到了一个取消引用的错误。这可能是一个相当基本的问题,但解决这个问题的最佳方法是什么?我在实现removeHigh方法时也遇到了一些问题,该方法旨在从队列中移除最高的元素。

  • 一般来说,如果我理解正确的话,在给定列表和添加每个元素之间的“heapizing;o(n)”运行时是有区别的;o(lg n)。java遵循这种行为吗?如果不是,下面的问题可能无效。 下面的示例似乎创建了一个"min-heap"。 然而,假设我想构建一个“最大堆”,但是构造函数不允许我同时传入集合和比较器。在这种情况下,构建最大堆的唯一方法是创建一个实现可比的包装器类吗? 注意:我知道可以用比较器创