【问题标题】:Implementing Priority queue using hashmap使用 hashmap 实现优先队列
【发布时间】:2014-08-13 20:48:39
【问题描述】:

这可能是一个非常幼稚的问题或没有意义。但我真的对优先队列的实现感到困惑。 为什么我们不能使用以键为优先级、以值作为具有该优先级的数据的哈希图。 我在这里缺少一些基本的东西吗? 例如: 如果 A 的优先级为 1,B 为 3,c 为 4 我们可以简单地将 1,3,4 作为键,A,B,C 作为 hashmap 的值,分别用于优先级队列实现。 请清除我的逻辑

【问题讨论】:

  • 因为HashMaps 没有被排序,所以你需要一些其他方法来确定什么是“下一个”。
  • 请注意,PQ 的可能实现是使用 BST。那不过是一个排序的映射(SortedMap 在 java 中)。这里的排序顺序对于 PQ 的规范是必不可少的。
  • 如果其中一个答案对您有所帮助,请接受它作为正确答案。

标签: java hashmap priority-queue


【解决方案1】:

优先级队列的目的是能够最快地访问最高优先级的元素,而 HashMap 的目的是允许您将键映射到值,因此可以在 O(1) 时间复杂度内访问这些元素。

O(1) 时间复杂度来自 Hash 函数,该函数为任何输入计算 HashMap 中的键或“索引”。但是哈希函数和优先级队列的结合变得复杂,因为HashMap是根据哈希函数分配key的,而优先级队列是根据元素的优先级分配key的,所以在结合的时候,相互矛盾。

【讨论】:

    【解决方案2】:

    priorityQueue 使您的数据保持排序并维护该结构,如果您要遍历priorityQueue,它将以您实现其构造函数的方式一致地返回您的数据。 HashMap 不保留插入顺序。如果你试图从你的 hashmap 返回所有元素,你会得到不一致的结果。

    除此之外,priorityQueue 使用最小/最大堆来保持数据有序,因此,任何插入/删除都是 Log(n),因为它必须从 log(n) 级别比较节点以维护 priorityQueue 属性。其中 n 是队列中的数据数。

    也许您可以使用linkedHashMap 来保留数据的顺序,因为它使用双重linkedList 结构。但是,对于 getMax() 等关键操作,运行时比priortyQueue 差。 priorityQueue 将在 O(log(n)) 时间内完成,而linkedHashMap 将花费 O(n) 时间,因为现在您必须遍历整个linkedList 以找到最大/最小值。

    希望对你有帮助

    【讨论】:

      【解决方案3】:

      该实现的问题在于它没有利用 HashMap 功能轻松访问给定键的任何元素。由于您正在创建队列,因此您以后不会有优先级 1、3、4,而只需要找到地图的最高优先级,必须完全迭代才能做到这一点!

      地图的工作原理是这样的:它创建一个巨大的数组,所有位置都包含空值(非常非常大)。然后,它使用公式获取您的密钥,通常是字符串或字符串表示形式,并将其转换为数字。一个简单的方法是对每个字母的字符代码求和,尽管这对地图来说是一个糟糕的选择,您可以稍后查找原因。然后,它用数组的模数(余数)大小(例如,Java 中的 n % 大小)限制该数字,使其成为数组中的有效位置,然后将元素放在那里。这样,就很容易进行密集搜索,因为您不需要搜索,您就知道每个元素的确切位置。

      因此,使用 Map 实现队列会占用大量内存,而没有 HashMap 搜索的明显优势。此外,迭代它非常非常昂贵,因为您需要迭代一个巨大的数组并忽略空值;您不确定实际值在哪里。想象在您的示例中,优先级从 0 到 100。即使您永远不会有 100 个任务,但您将有一个 100 个位置数组来迭代,检查每个位置是否有具有该优先级的任务。

      更好的方法是使用简单的列表并将优先级存储在数据中。假设优先级不随时间变化,您只需在添加时迭代一次,以找到正确的位置,并始终访问第一个(或最后一个)元素,即具有已知最高优先级的元素。

      关于性能的最后一点,最好在添加时进行迭代,即使您添加的时间量与您正在搜索的时间量完全相同。因为当你搜索最高优先级时,你每次都需要一直走到最后,因为也许最后一个元素是更大的那个。请注意,即使您的键是数字字符串或整数,HashMap 也不会以整齐的新月顺序存储您的项目。它是专门设计的,以避免冲突(两个相似的对象具有相同的键,因此需要将列表添加到大数组的特定位置)。但是,当您添加时,假设您的列表已经排序,您只需要迭代直到到达正确的目的地。

      【讨论】:

      • 谢谢它是有道理的“只需要找到地图的最高优先级”,我想检查从最高优先级到最低优先级。如果最大值不存在,则转到第二高,依此类推..绝对列表更好。感谢您的澄清
      • 感谢您的快速回答:)
      • 考虑一下。事实证明,List 在“添加”时进行了一次迭代。 hashmap 也在进行一次迭代,即在“获取”时。所以这两种情况都需要一次迭代。
      • 您能否详细说明一下,在上述情况下列表如何更好,时间复杂度明智。
      • 用新信息更新答案,看看:)
      【解决方案4】:

      长话短说,这一切都归结为HashMaps 没有排序,因此无论如何您最终都需要一些外部机制来确定队列中的“下一个”是什么。

      例如,假设您输入了条目

      [1, "A"]
      [3, "B"]
      [4, "C"]
      

      如果您只有一个 HashMap,则没有可靠的方法可以确定 1 是“第一个”没有遍历每个键的,因为在内部键可能是:

      [1, "A"]
      [3, "B"]
      [4, "C"]
      

      [3, "B"]
      [4, "C"]
      [1, "A"]
      

      [3, "B"]
      [1, "A"]
      [4, "C"]
      

      等等。

      来自HashMap javadoc

      这个类不保证地图的顺序;特别是,它不保证订单会随着时间的推移保持不变。

      因此,使用HashMap 确实没有意义,因为无论如何你必须做的额外工作最终会使HashMap 的效率低于堆。


      现在,如果您真的想使用HashMap,您可以:

      • 获取EntrySet,按键排序,然后得到第一个值。如果这是您需要获取第一个值的 only 时间,那没关系,但这种情况很少发生。一旦你改变了map,需要再做一次,效率比普通堆还差。

      • 使用一些外部机制,例如ArrayList 或其他东西来跟踪键,也许将其作为插入值的一部分进行排序。不过,此时不妨跳过HashMap 并“正常”实现堆,因为否则您基本上在做同样的事情,但会增加在ArrayListHashMap 之间拆分数据的复杂性。

      所以最后,额外的工作并不值得。

      【讨论】:

      • 感谢您的快速回答:)
      【解决方案5】:

      当您尝试遍历 HashMap 时,您会得到一个奇怪的顺序。 HashMaps(或与此相关的任何地图)没有保证遍历顺序,因此您将无法以有效的方式“排序”队列。

      您当然可以查询所有键,对该列表进行排序,并按顺序获取值,但与替代方案相比,这非常低效(您已经在 O(n^2) 的键长度上,而无需执行其他操作工作)

      【讨论】:

      • 感谢您的快速回答:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多