【问题标题】:Java: Count fast a number of items shared among multiple setsJava:快速计算多个集合之间共享的项目数量
【发布时间】:2016-11-07 16:33:37
【问题描述】:

我在输入上有 10 个集合,其中每个集合包含数百个项目(字符串)。

我想要什么:

我想查找至少由两个集合共享的项目,并按在不同集合中出现的次数以降序对它们进行排序。

我的做法:

我创建了以下代码。但是,我想知道,是否存在更有效的方法来做到这一点......

Map<String, Integer> sharedCounts = new HashMap<>();

for (int i = 0; i < 10; i++) {
  Set<String> words = getWords(i);
  for (String word : words) {
    if (sharedCounts.containsKey(word)) {
      sharedCounts.put(word, commons.get(word) + 1);
    } else {
      sharedCounts.put(word, 1);
    }
  }
}

Map<String, Integer> sorted = sharedCounts.entrySet().stream()
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 

【问题讨论】:

  • 看起来不错。 Map 的 putcontainsKey 具有 O(1) 时间复杂度,否则您对所有单词的所有集合仅迭代一次,给出 O(n) 其中n 是所有集合中单词的总数。最慢的部分实际上是排序,因为这将是O(nlogn)
  • 顺便说一句,我刚刚意识到一件事-您的sharedCounts 地图与sorted 地图有何不同?它们都是哈希映射,这意味着无序。整个排序是多余的。不想收藏上榜吗?或者也许是树状图,但让 count 成为 key 和 word 成为 value?
  • 是的,您必须生成树状图。我刚试过,它按字母顺序排序,不管哪个单词出现次数更多。
  • 为什么在放地图之前要排序?
  • 变量sharedCountscommons有什么区别?那些应该是同一个对象吗?

标签: java set


【解决方案1】:

计算计数的算法具有 O(SUM(Ni)) 的渐近复杂度,其中 Ni 是第 i 组单词的大小。这是最快的速度。

您似乎缺少一个过滤步骤,在该步骤中您丢弃计数为 1 的单词。

Map<String, Integer> sorted = commons.entrySet().stream()
    .filter(e -> e.getValue() > 1) // <<== Add this line
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 

总体复杂度为 O(SUM(Ni)+Ndistinct-dup*log(Ndistinct-dup)),其中 Ndistinct-dup 是具有重复的不同单词的数量。

【讨论】:

  • 这是一个很好的实际改进,但理论时间复杂度仍保持为O(nlogn)。我不确定我们是否可以做得比这更好。
  • @JaroslawPawlak 我添加了过滤以使算法返回 OP 想要的(即至少由两组共享的项目),而不是提高运行时间,当然,O(N *logN) 最坏的情况。
  • 如果订单是O(SUM(Ni)+Ndistinct-dup*log(Ndistinct-dup)),那么我们仍然不知道哪个部分更慢,O(SUM(Ni))O(Ndistinct-dup*log(Ndistinct-dup))。如果Sum(Ni) 很大而Ndistinct 很小,那么加快计数部分的速度会很有用。有办法做到这一点here
【解决方案2】:

您可以简单地使用 oneSet.addAll(anotherSet) 组合所有集合,然后填充您的 sharedCounts 映射,而不是遍历每个 Set 中的所有元素

Set<String> combinedSet = new HashSet<String>(); 

for (int i = 0; i < 10; i++) {
  Set<String> words = getWords(i);
  combinedSet.addAll(words);
}

Map<String, Integer> sharedCounts = new HashMap<>();

for (String word : combinedSet) {
  for (int i = 0; i < 10; i++) {
     Set<String> words = getWords(i);
     if (words.contains(word)) {
        if (sharedCounts.containsKey(word)) {
           sharedCounts.put(word, commons.get(word) + 1);
        } else {
           sharedCounts.put(word, 1);
        }
     }
   }
}

【讨论】:

  • 你将如何创建sharedCounts 地图,其中包含所有单词且没有重复的单个集合?
  • @JaroslawPawlak 抬头,我提到了类似的东西
  • 所以现在想一想哈希集contains 的时间复杂度是多少,以及你这样做了多少次。您提出的代码并不比 OP 的代码更短或更易读,而且性能也差很多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-15
  • 2018-03-18
  • 2012-07-22
  • 1970-01-01
  • 1970-01-01
  • 2011-02-02
  • 1970-01-01
相关资源
最近更新 更多