【问题标题】:TreeSet: number of elements less than a value efficientlyTreeSet:有效地小于一个值的元素数
【发布时间】:2015-12-23 02:35:40
【问题描述】:

我需要一种方法来非常快地计算 TreeSet 的整数中小于 X 的元素数。

我可以使用

  • 子集()
  • headSet()
  • tailSet()

方法,但它们真的很慢(我只需要计数,而不是数字本身)。有什么办法吗?

谢谢。


编辑:

我找到了一种使事情变得更快的解决方法!我正在使用 BitSet 和它的 cardinality() 方法。我首先创建了一个 BitSet,对于添加到 TreeSet 的每个元素,我在 BitSet 中设置了相应的索引。现在,要计算我使用的小于 X 的元素数:

bitset.get(0, X+1).cardinality()

这比 treeset.subSet(0, true, X, true).size() 快得多。

有人知道为什么吗?我假设 BitSet.cardinality() 不使用线性搜索。

【问题讨论】:

  • 你可以试试 Guava TreeMultiset,它支持 headMultiset(element).size() 在 O(log n),而不是 O(n)。不过,它与TreeSet 不太一样。但是headMultiset(element).elementSet().size() 也是 O(log n)。
  • 为什么需要树集?您是否经常更新数据结构?如果您不更新数据结构,只需在 hashmap 中保持元素数量小于 X 即可!如果您不经常更新它,请保留一个排序的数字链接列表。在插入/删除时,在 O(1) 中从列表中添加/删除并更新哈希图 (O(n))。
  • 感谢您的评论@Masood_mj。问题是 X 不是一个特定的值,每次我调用 cardinality() 函数时它都会改变。因此,如果我想使用哈希图,那么每次我将 Y 添加或删除到哈希图中(+1 或 -1 全部)时,我都必须使用 key > Y 更新所有项目。我错过了什么吗?
  • Hashmap+链表是 O(1) 获取和 O(N) 更新。通过使用(排序的)二叉树,您可以获得 O(Log(n)) 获取和 O(Log(n)) 更新。在树的每个元素中,还保留其后代的数量。现在要获得# items
  • 好主意@Masood_mj,有没有办法在Java中拥有这样的二叉树或者我必须自己实现它?

标签: java count subset treeset


【解决方案1】:

由于到目前为止所有答案都指向不同于 Java 的 TreeSet 的数据结构,我建议使用 Fenwick 树,它具有 O(log(N)) 用于更新和查询;有关 Java 实现,请参阅 link

【讨论】:

    【解决方案2】:

    “真正快”需要多快?你大概有多少个元素?

    subSet()/headSet()/tailSet() 是 O(1),因为它们返回原始树集的视图,但如果您 size() 您的 subSet() 您仍在迭代所有原始元素,因此 O(N)。

    您使用的是 Java 8 吗?这将大致相同,但您可以并行计算成本。

    Set<Integer> set = new TreeSet<>();
    // .. add things to set
    
    long count = set.parallelstream().filter(e -> e < x).count();
    

    注意编辑

    通过进一步的探索和测试,我无法证实“如果您 size() 您的 subSet() 您仍在迭代所有原始元素”的说法。我错了。这台 4 核机器上的 parallelstream().count()subSet().size() 慢约 30%

    【讨论】:

    • 谢谢!我有十万个元素!我不知道 count(),我认为使用 subSet 是问题。
    • 您对 subview count() 方法,或者我假设您的意思是 `size() 方法迭代整个原始集合的说法有何支持?
    • 感谢您的提问。我见过像stackoverflow.com/questions/15703120/…stackoverflow.com/questions/14290751/… 这样的答案,但是当我调查时,我无法根据源代码证实这些说法 - 版本可能已经改变等等。事实上,编写我自己的测试 size() 似乎并没有太大变化即使 N 变化 x100。我会进一步看 - 可能会撤回答案 - @mnmp 你发现任何改进了吗?
    • 这里 size() 的另一个 O(N) stackoverflow.com/questions/14750374/…
    【解决方案3】:

    如果你不更新数据结构,只要保持hashmap中元素个数小于X即可!

    如果您不经常更新它,请保留一个排序的数字链表。在插入/删除时,在 O(1) 中从列表中添加/删除并更新哈希图 (O(n))。

    通过使用(排序的)二叉树,您可以获得 O(Log(n)) 获取和 O(Log(n)) 更新。在树的每个元素中,还保留其后代的数量。现在要获得# items

    顺便说一句,如果你愿意接受大致的答案,也可以有更快的方法。

    【讨论】:

      【解决方案4】:
      package ArrayListTrial;
      
      import java.util.Scanner;
      
      public class countArray {
      
          public static void main(String[] args) {
              // TODO Auto-generated method stub
      
              int[] array = new int[100];
              Scanner scan = new Scanner(System.in);
              System.out.println("input the number you want to compare:");
              int in = scan.nextInt();
              int count = 0;
              System.out.println("The following is array elements:");
              for(int k=0 ; k<array.length ; k++)
              {
                  array[k] = k+1;
                  System.out.print(array[k] + " ");
                  if(array[k] > in)
                  {
                      count++;
                  }
              }
              System.out.printf("\nThere are %d numbers in the array bigger than %d.\n" , count , in);
      
          }
      
      }
      

      【讨论】:

      • 也许这是另一个问题的答案?
      • 这不是任何问题的答案。正在搜索的数组全是零。因此,任何特定值的计数都是预先知道的:不需要搜索。 @KarlM
      猜你喜欢
      • 2021-04-14
      • 2013-08-13
      • 2023-03-25
      • 1970-01-01
      • 2021-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多