【问题标题】:When is using a TreeSet faster than a HashSet?什么时候使用 TreeSet 比使用 HashSet 更快?
【发布时间】:2021-12-25 05:43:34
【问题描述】:

我一直在阅读这个主题,到目前为止,从我对添加、删除和搜索操作的理解来看,HashSet 的时间复杂度为 O(1) 更快,而 TreeSet 的时间复杂度为 O(log n)。在遍历元素时,HashSet 和 TreeSet 的时间复杂度都是 O(n)。

那么当 TreeSet 比 HashSet 快时,什么是用例?

【问题讨论】:

  • 您为什么认为存在这样的用例?

标签: java data-structures computer-science


【解决方案1】:

一般来说,您可以通过查看 Java 容器类实现的接口来最好地比较它们的功能。检查the HashSet javadoc,你会看到它有Iterable<E>, Collection<E>, Set<E>TreeSetIterable<E>, Collection<E>, NavigableSet<E>, Set<E>, SortedSet<E>

所以区别是SortedSetNavigableSet。这些是 TreeSet 提供而 HashSet 不提供的方法。如果您反过来查找他们的 javadoc,您会发现一系列被组织起来利用 TreeSet 中元素顺序的行为。 HashSet 没有元素排序的概念。这是主要的区别。如果要对元素施加顺序,通常仅限于单独对它们进行排序,而按自然顺序遍历 TreeSet 是每个项目的摊销常数时间。 (遍历的各个步骤可能需要时间比例 log n。)

在实践中,没有太多用例表明 O(1)HashSet 性能的预期摊销时间与 O(log n)保证之间的差异TreeSet 的时间对于它们共有的方法很重要。请记住 log_2(n) 几乎在所有实际用途中都小于 40。执行几条指令 40 次通常会影响调用算法的性能。

当差异很重要时,您仍然需要考虑散列性能的预期摊销性质,因为任何给定的add() 可能需要 O(n ) 是时候扩展内部存储桶数组并重新散列所有内容了。在某些应用程序中,这是一个杀手。例如,您的游戏通常像闪电一样运行,但偶尔会出现卡顿,而 10 Mb 哈希集增长到 20 Mb。同样,如果您的数据恰好与 HashMap 的散列函数配合不佳(或者数据可能来自故意尝试破坏它的恶意用户),则性能可能更像 O(n) 而不是 O(1)。

TreeSet 的性能没有这么大的性能怪癖。例如。重组红黑树所花费的时间仅与 log_(n) 成正比,而且这种情况很少见。也就是说,后来版本的 HashSet 实际上使用树集作为存储桶,以避免被坏人利用。

【讨论】:

    【解决方案2】:

    从技术上讲,两者无法公平比较。 HashSet 实现 Set,而 TreeSet 实现 NavigableSet,NavigableSet 具有基于其元素概念的额外功能(尽管不要求实现对它们进行实际排序)。

    对于所有 Set 方法,HashSet 比 TreeSet 更快(O(1) vs O(log n)。

    TreeSet 提供 NavigableSet 方法(例如,O(log n) 的 ceiling(),它“更快”只是因为它们不存在于 Set,因此没有竞争。

    TreeSet 也会在 O(n) 时间内以 Comparable 顺序迭代其元素,而 HashSet 则不能这样做;您必须遍历 Set 以收集列表中的元素,然后对列表进行排序 - 有效时间复杂度 O(n log n)。

    【讨论】:

      【解决方案3】:

      在排序与您正在执行的任务相关的某些用例中,TreeSetHashSet 更快。

      例如,如果我有一组字符串,并且我想找到集合中大于或等于给定字符串的最小(根据排序)字符串。

      • 使用HashSet,我必须遍历整个集合以找到字符串...在给定字符串不在集合中的情况下。那是O(N)
      • 使用使用所需排序的TreeSet,我可以使用ceilingO(logN) 中找到所需的字符串。

      另一个例子,如果我想迭代字符串集按顺序,那就是O(N) 用于TreeSet。对于HashSet,我必须将字符串提取到一个数组中,对数组进行排序,然后对其进行迭代。总之就是O(NlogN)


      注意事项:

      1. 复杂性和性能不是一回事。例如,当N 相对较小时,O(N) 解决方案可能比O(NlogN) 解决方案更快。

      2. 当集合大小超过 231 时,Java HashSet 操作不再是 O(1),因为标准的 HashSet 实现使用 Java 数组作为哈希数组,并且无法调整大小超出该值.

      【讨论】:

      • 在注意事项 1 中,您的意思是“O(NlogN) 解决方案可以比 O(N)... 更快”吗?
      • 是的。这就是我的意思。如果你分析大 O 符号的数学定义,你就会明白为什么。
      【解决方案4】:

      TreeSet 的附加价值不是复杂度而是数据结构的类型。 hashset的复杂度在任何情况下都比treeset好,除了在迭代的情况下,它们的复杂度是一样的。

      哈希集: add、remove 和 contains 方法具有恒定的时间复杂度 O(1)。

      TreeSet: add、remove 和 contains 方法的时间复杂度为 O(log (n))。

      TreeSet 提供了一些 hashset 没有的方法,例如处理有序集合,如 first()、last()、header()、tailset()。

      所以为了解决一些问题,TreeSet 更适合,所以你的程序性能会比你使用 HashSet 更好。

      【讨论】:

        【解决方案5】:

        TreeSetHashSet 上定义的实际方法中,没有一个在TreeSet 上确实更快。 TreeSet 上的其他方法无法在 HashSet 上高效实现,因此它们不是 - 诸如 floorceiling 之类的方法。

        【讨论】:

          猜你喜欢
          • 2012-02-15
          • 2010-11-17
          • 2013-04-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-07
          • 1970-01-01
          • 2011-05-29
          相关资源
          最近更新 更多