【问题标题】:Why populating priority queue in java using Offer() populates it in alphabetical order while it also gives priority to capitalized words?为什么使用 Offer() 在 java 中填充优先级队列会按字母顺序填充它,同时它还优先考虑大写单词?
【发布时间】:2025-12-12 11:35:01
【问题描述】:

我很困惑。 Offer() 按字母顺序填充列表,并且它也优先考虑大写单词。如何按我输入的顺序填充列表?

import java.util.*;
public class Bucky {

 public static void main(String[] args) {
  PriorityQueue<String> q= new PriorityQueue<String>();
  q.offer("first");
  q.offer("aecond");
  q.offer("zhird");

  System.out.printf("%s ",q);
  System.out.println();
  System.out.printf("%s ", q.peek());
  System.out.println();


 }

}

o/p= [aecond,第一,zhird] 第二个


import java.util.*;
public class Bucky {

 public static void main(String[] args) {
  PriorityQueue<String> q= new PriorityQueue<String>();
  q.offer("first");
  q.offer("aecond");
  q.offer("Zhird");

  System.out.printf("%s ",q);
  System.out.println();
  System.out.printf("%s ", q.peek());
  System.out.println();


 }

}

o/p= [Zhird,第一,第二] 日德

【问题讨论】:

  • 它是基于String#compareTo 排序的,它“按字典顺序比较两个字符串”(基于 unicode 值)。您必须传入自己的比较器才能更改排序。
  • 我明白为什么大写优先,但我对firstaecond 之间的重新排序感到困惑。
  • @shmosel 那是因为iterator() 不会以任何特定顺序遍历元素。来自docs方法 iterator() 中提供的迭代器不能保证以任何特定顺序遍历优先级队列的元素。如果您需要有序遍历,请考虑使用 Arrays.sort(pq.toArray())
  • @FedericoPeraltaSchaffner:多么令人困惑的文档说明。迭代器实际上确实保证了遍历顺序:项目出现在底层数组中的顺序。注释的真正含义是迭代器不能保证按优先级顺序遍历项目。这是因为任何包含两个以上元素的二进制堆都有多个有效的数组表示。
  • @JimMischel 我已经阅读了您的答案,我觉得它很棒。我认为它解释了为什么不能保证订单。我会尽快投票(今天票数用完了)。我认为文档说明清楚地表明不能保证顺序,甚至说不能保证以任何特定顺序遍历元素。它没有提到优先级。如果遍历顺序是特定于实现的,则无法保证。事实是,优先级队列并不打算用作对其元素施加顺序的结构,除了第一个。

标签: java data-structures queue


【解决方案1】:

我认为您要质疑的是数组中项目的顺序。也就是说,当您执行System.out.printf("%s ",q); 行时,您会得到输出[Zhird, first, aecond],并且您想知道为什么数组中的第二项是first,而它在字典上大于aecond

答案在于优先级队列的结构。它的结构为binary heap,并受制于二进制堆的构造方式。

您的代码像这样构造堆:

q.offer("first");
q.offer("aecond");
q.offer("Zhird");

如果我们把堆想象成一棵树,你可以更容易地看到发生了什么。第一次调用将"first" 放在树的根部。创建二叉堆的规则说,在下一次调用时,字符串"aecond" 将被放置在最低、最左边的位置,然后重新排列树。所以首先你有:

       "first"
      /
  "aecond"

然后,你沿着树往上走。如果孩子比父母小,那么你交换父母和孩子。所以在这种情况下,你最终得到了树:

       "aecond"
      /
  "first"

现在,字符串"Zhird" 出现了。它被添加到未被占用的最低、最左边的位置:

       "aecond"
      /       \
  "first"   "Zhird"

又到了重新排列树的时候了。 "Zhird" 在字典上小于 "aecond",所以它们被交换了:

       "Zhird"
      /       \
  "first"   "aecond"

请注意,这仍然是一个有效的堆。子节点大于父节点。

二叉堆的数组表示基本上只是对树的广度优先遍历,所以这棵树表示为["Zhird", "first", "aecond"]

如果您要从堆中一个接一个地删除项目,您会以正确的顺序(即“Zhird”、“aecond”、“first”)获取它们。但是数组表示可能与严格的字典顺序有很大不同,因为删除过程会重新排列树。实际上,如果您要从队列中删除第一项(即调用q.poll),然后输出数组,您会看到它已被重新排序为["aecond", "first"]

请参阅我的博客http://blog.mischel.com/2013/09/29/a-better-way-to-do-it-the-heap/,了解堆插入和删除规则。请参阅后续文章 http://blog.mischel.com/2013/09/30/a-simple-heap-of-integers/,了解有助于您了解树如何映射到数组的实现。

此外,如果您要按此顺序填充堆:

q.offer("Zhird");
q.offer("first");
q.offer("aecond");

输出将是[Zhird, first, aecond]。但如果您的订单是:

q.offer("Zhird");
q.offer("aecond");
q.offer("first");

输出将是[Zhird, aecond, first]

【讨论】:

  • 知道了。但是如何以输入条款的方式打印列表?有什么直接的方法,还是我必须像树一样按前缀顺序打印?
  • @Anuragkush:如果要按优先级顺序输出队列的内容,则可以删除每个项目并输出它,或者在单独的数组中创建一个副本,然后使用用于创建队列的相同比较器。以前缀顺序遍历树不会给你你正在寻找的顺序。
【解决方案2】:

如果要忽略大小写,简单如下:

new PriorityQueue<>(String.CASE_INSENSITIVE_ORDER);

【讨论】: