【问题标题】:How to avoid O(N^2) when comparing elements from 2 lists?比较 2 个列表中的元素时如何避免 O(N^2)?
【发布时间】:2011-04-20 04:06:05
【问题描述】:

我有 2 个具有相同对象类型的列表。

List A [ foo, bar, moo, woo, pee ]

List B [ bar, woo ]

我想比较这两个列表,如果名称匹配, 将其属性设置为 true。

例如,

if(ListA[1].name.equals(ListB[0].name)) { //match name 'bar' and 'bar'
    ListA[1].hasSameName = true;
}

类似的东西。

我可以写出 O(N^2) 的解。

for(Talent checkedTalent : ListA) {
    for(Talent filteredTalent : ListB) {
        if( checkedTalent.Id.equals(filteredTalent.Id) ) {
            filteredTalent.isSelected = true;   
        }
    }
}

这可以更有效地完成吗?

【问题讨论】:

  • 快速但愚蠢:排序和比较。
  • 订单重要吗?会有骗子吗?因为在这些情况下,琐碎的事情甚至可能不是真的。如果没关系并且没有欺骗,我建议使用 Set 而不是 List。
  • @amit:不会有重复的值,顺序无关紧要。
  • 然后我投票支持建议的哈希解决方案,但我仍然认为(至少为了可读性)使用 Set 比 List 更合适(除非它是关键的性能问题并且不能采取插入开销)
  • @amit:我也同意,使用 Set 似乎更好。

标签: java algorithm list


【解决方案1】:

对 O(n) 解决方案使用散列(假设一个高效的散列实现):

Set<String> ids = new HashSet<String>(ListA.size());
for(Talent checkedTalent : ListA) {
    ids.add(checkedTalent.Id);
}
for(Talent filteredTalent : ListB) {
    if (ids.contains(filteredTalent.Id)) {
        filteredTalent.isSelected = true;
    }
}

【讨论】:

  • HashSet 有一个构造函数,它接受任何 Collection 作为参数,这将使您不必实现第一个循环(尽管您仍然会产生开销)。哦 - 我看到您将每个人才映射到一个 ID。我没有意识到这一点。别管这个评论。 :)
【解决方案2】:

注意 O(n2) 解决方案具有时间复杂度,因为对于来自 ListA 的每个元素,您必须检查 ListBall(再次)。如果您能以某种方式在 ListB 上查找来自 ListA 的当前元素,则可以减少到 O(n)。您可以使用的一种数据结构是Map

因此,如果您从其中一个列表中构建一个Map 并遍历另一个列表,查找Map 中的每个元素以查看是否匹配,您可以将整体时间复杂度降低到O(n ) - 以 O(n) 空间为代价。

例如:

Map<String, Talent> map = new HashMap<String, Talent>();

for(Talent t : ListA)
{
    t.put(t.id, t);
}

for(Talent t : ListB)
{
    if(map.containsKey(t.id))
    {
        t.isSelected = true;
    }
}

【讨论】:

  • 可以Set,但必须是,如上例所示。总体时间复杂度是相同的 - 但是,我承认使用 Set 的答案更清晰。
  • 不适用于HashMap,没有add() 方法。您必须使用put(t.id, null),然后检查是否map.keySet().containsKey(t.id)。您可能想要的是 Set 而不是 Map
  • 呃!这就是我应该睡觉的时候在附近闲逛的结果:/对不起@John,谢谢@amit,@aroth。现在修好了。
【解决方案3】:

然后排序 (2*(n log n)),然后沿着每个列表走 (2*n)。

【讨论】:

    【解决方案4】:

    有关如何更有效地执行此操作的一些灵感,请阅读如何实现 SQL 连接,即Nested loop join,一个带有 b 树索引的嵌套循环连接(排序一并通过它进行二分搜索)另一个元素中的每个元素)、Merge joinHash join。相同的概念。

    【讨论】:

      【解决方案5】:

      您可以从第一个 List 创建一个 Set:

      Set mySet = new HashSet(listA);
      

      现在你遍历 listB:

      for(Object foo : listB)
          if(mySet.contains(foo))
              foo.isSelected = true
      

      我认为这是 O(n * lg n),但我不会提供证明。 :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-07
        • 1970-01-01
        • 2022-01-23
        • 2011-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多