【问题标题】:How to iterate over a PriorityQueue?如何迭代 PriorityQueue?
【发布时间】:2011-12-29 01:42:10
【问题描述】:
for (Event e : pq)

不按优先级顺序进行迭代。

while(!pq.isEmpty()){
  Event e = pq.poll();
}

这可行,但会清空队列。

【问题讨论】:

  • 如何清空队列? peek() 不会删除元素。
  • peek() 不应该删除对象
  • peek() 继续返回头部
  • while 循环中的逻辑没有意义。 isEmpty 不像 hasNext。
  • 有类似的问题。修复了使用委托和包装器概念stackoverflow.com/a/51968633/1465553

标签: java collections loops priority-queue


【解决方案1】:

来自Javadocs

iterator() 方法中提供的迭代器不能保证以任何特定顺序遍历 PriorityQueue 的元素。如果需要有序遍历,可以考虑使用Arrays.sort(pq.toArray())。

可能还有其他等效机制。

【讨论】:

  • 这个答案将受益于一个例子。据我所知,Arrays.sort(pq.toArray()) 什么也没做。首先必须创建一个包含PriorityQueue 元素的数组,然后可以使用Arrays.sort(theActualArray, pq.comparer()) 对该数组进行排序。
  • @Madmenyo 1. 这个答案确实有一个例子。 2.Arrays.sort() 不是什么都不做。 3.没有PriorityQueue.comparer()这样的方法。
  • 这种用法的计算量非常大。出于学术目的,我想补充一点,使用 this 迭代排序优先级所需的时间是 n * n * log(n);其中第一个 N 是遍历队列的时间,N * log(N) 用于排序。
  • @LanVukušič 不,不是。 pq.toArray() 需要 O(n)(它只是复制堆的底层数组)并且 then 排序是 O(n log(n))。它一起给出 O(n + n log n) = O(n log n)
  • 我在 Java doc 中找到了相同的描述,没有任何可执行示例
【解决方案2】:

由于底层实现(我认为它是 Java 中的最小堆),您不能按该顺序遍历 Priority Queue。

它不是一个排序数组,这样您就可以从一个元素转到优先级较低的元素。

Peeking(读取堆中的顶部元素)是常数时间O(1),因为它查看的是最小的元素。

要获得第二个下一个,您必须将最小的顶部元素出列,这就是它的工作原理。
Dequeing (re-heapify = O(log n) time) 不仅仅是取出那个元素的问题,底层结构会重新排列自己,以便将优先级最低的元素放在首位。

另外,要遍历整个优先级队列以按排序顺序读取所有项目,这是一个O(n log(n)) 操作。
所以你不妨抓住队列中的所有元素并对它们进行排序(也可以O(n log (n))),然后你可以随意浏览它们。唯一的缺点是您持有队列的额外副本。

不过,如果您需要以这种方式遍历数据,则优先级队列可能不是满足您需求的正确数据结构。

【讨论】:

  • 那么PriorityQueue 几乎没用,那么?
  • 一点也不,它非常有用,并且针对它的实际用途进行了很好的优化。创建比完全排序的数据结构(线性与 O(n log n))更快,但您仍然可以在恒定时间内找到最小值/最大值,并在 log n 中找到入队/出队。如果您需要按排序顺序遍历所有元素,这根本不是正确的数据结构。
  • 公平,尽管我想说它的用途非常有限/特定。
  • @Qix 并不总是迭代是一个糟糕的设计; OP 想要遍历所有元素,但有时我需要从优先级队列中找到与某个谓词匹配的第一个元素。然后,对 10k 数组进行排序以获取前 10 个元素中的一个是非常低效的(尤其是在循环中完成时)
【解决方案3】:

基于堆的优先级队列只保证第一个元素是最高/最低的。没有廉价(即 O(n))的方法来以排序形式获取元素。

如果您需要经常这样做,请考虑使用以排序形式维护元素的结构。例如,使用java.util.TreeSet,并使用pollFirst() 或pollLast() 代替peek() / poll()

【讨论】:

    【解决方案4】:

    以前的海报说了一切,但没有人给出完整的工作示例(除了复制 pq),所以这里是:

    Event[] events = pq.toArray(new Event[pq.size()]);
    Arrays.sort(events, pq.comparator());
    for (Event e : events) {
        System.out.println(e);
    }
    

    【讨论】:

      【解决方案5】:

      您可以复制队列并循环轮询,在此示例中 pq 是原始优先级队列:

      PriorityQueue<Your_class> pqCopy = new PriorityQueue<Your_class>(pq);
      while(!pqCopy.isEmpty()){
          Your_Class obj = pqCopy.poll();
          // obj is the next ordered item in the queue
          .....
      }
      

      【讨论】:

        【解决方案6】:

        最近,我遇到了同样的问题。我想使用优先级队列中的一些特定对象,然后保留剩余的元素。

        1) 我创建了一个 newPriorityQueue。 2) 使用 Iterator 解析 oldQueue 中的每个元素 3) 使用 oldQueue.poll() 方法检索元素 4) 如果不使用,将元素 3) 插入到 newPriorityQueue。

         Queue<String> newQueue = new PriorityQueue<String>(); 
            // Assuming that oldQueue have some data in it.
        
            Iterator<String> itr = oldQueue.iterator();
            while(itr.hasNext()){
                String str = oldQueue.poll();
                // do some processing with str
                if(strNotUsed){
                    newQueue.offer(str);
                }
            }
        

        最后,oldQueue 将是空的。 @其他人:-如果我能做同样的事情,请提出更好的方法。我不能使用迭代器,因为它不会以正确的顺序返回元素。

        【讨论】:

          【解决方案7】:

          因此,如上所述,将 priorityQueue 放入 List 中,然后对其进行排序是一个不错的选择。以下是迭代器给出意外结果的一些细节:

          迭代器不会以正确的顺序返回元素,因为它从底层数据结构(类似于 ArrayList)中打印。 ArrayList 中存储数据的方式与将数据存储在 BinaryHeap 的 Array 实现中的方式相同。例如:

          PriorityQueue<Integer> pq = new PriorityQueue<>();
          ArrayList<Integer> test = new ArrayList(Arrays.asList(6,12,7,9,2));
          test.forEach(x -> pq.add(x)); 
          System.out.println("Priority Queue:- "+pq); [2, 6, 7, 12, 9]
          

          childOf(i) 是 2*i+1 和 2*i+2 并且 parentOf(i) 是 (i-1)/2

          【讨论】:

            【解决方案8】:

            受@carlos-cuesta 的启发,实际上很容易构造一个排序的迭代器,而无需消耗原始的PriorityQueue。例如。我们可以将其表示为 kotlin 中的扩展函数:

            fun <E> PriorityQueue<E>.sortedIterator() = sequence {
                val pqCopy = PriorityQueue(this@sortedIterator)
            
                while(pqCopy.isNotEmpty()) yield(pqCopy.poll())
            }
            
            somePQ.sortedIterator().next() // ...
            

            成功的关键是在轮询之前使用内置的复制构造函数复制优先级队列。

            我相信这也比大多数其他提供的解决方案更有效,因为在不只消耗完整迭代器的情况下。

            【讨论】:

              【解决方案9】:

              peek() 方法不会从队列中删除任何内容,但正因为如此,它会不断获取顶部值,直到它为空。 我猜你在你的 while 循环之后检查了它是否为空,这会给你这个结论。

              做到这一点的唯一方法是自己排序。 你可以像这样得到它的原始比较器:

              Event[] events = Arrays.sort(pq.toArray(), pq.comparator());
              for (Event e : events) {
                  // do stuff
              }
              

              【讨论】:

              • 这与OP的原始示例相同。
              • 我已经知道这个例子不是最好的路线。使用comparator,无需将队列转换为数组。 for (Event e : pq) { 应该可以完成这项工作。
              • Arrays.sort 有退货吗?我认为它返回void。我收到错误:Found void, expect .....
              • 这肯定不是唯一的方法。例如,他可以创建另一个 PQ。
              【解决方案10】:
              for (Event event: pq.toArray(new Event[pq.size()])) {
                  event.toString();
              }
              

              【讨论】:

              • 请编辑您的答案以包含一些解释。仅代码的答案对教育未来的 SO 读者几乎没有作用。您的答案因质量低劣而在审核队列中。
              • 您的代码没有解决问题。数组应该在迭代之前排序。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2018-08-06
              • 2016-04-09
              • 1970-01-01
              • 2021-06-22
              • 2010-11-02
              • 2012-03-23
              • 2011-05-27
              相关资源
              最近更新 更多