【问题标题】:Big O: what is the time complexity for this algorithm?Big O:这个算法的时间复杂度是多少?
【发布时间】:2016-06-30 05:35:03
【问题描述】:

下面我的方法的最佳情况和最坏情况时间复杂度是多少?

我知道 ArrayList.add() 的时间复杂度为 O(1),但不确定 loaded.stream().distinct().collect(Collectors.toList());

  public static int countUnique(WordStream words) {

    ArrayList<String> loaded = new ArrayList<String>();
    ArrayList<String> empty = new ArrayList<String>();

    // Fill loaded with WordStream words
    for (String i : words) {
      loaded.add(i);
    }

    empty = (ArrayList<String>) loaded.stream().distinct().collect(Collectors.toList());

    return empty.size();
  }

【问题讨论】:

    标签: java arraylist time-complexity java-stream collectors


    【解决方案1】:

    首先,ArrayList() 不是所有操作的 O(1),而是 add()

    distinct() 是 O(n),因为它必须检查所有元素。每次迭代都是 O(1),因为它是由一个 HashSet 支持的,它是 O(1)。

    您可以将代码替换为:

    return (int)loaded.parallelStream().distinct().count();
    

    这会快很多,但仍然是 O(n)

    【讨论】:

    • ArrayListnot O(1) for add()documentationadd() 指定为具有“摊销常数时间”,解释为“添加 n 个元素需要 O(n) 时间”,这就是 O(n) 的全部意义所在,因为我们只关注整个任务的复杂性,包括n 个元素。顺便说一句,我强烈怀疑.parallelStream() 在这里的好处。你应该做的最低限度是.parallelStream().unordered()(理论上count()意味着无序,但根据我的经验,你最好不要相信实现)
    • 在我的测试中,.parallelStream().unordered().distinct().count() 明显快于.parallelStream().distinct().count(),但很少达到顺序.stream().distinct().count() 的性能...
    • @Holger 有趣。我刚刚对 parallelStream() 的 4 个组合进行了测试(带有预热),并且在列表大小为 10 到 1M 的情况下,没有或没有 unordered():令人惊讶的是,.stream().unordered().distinct().count() 始终明显更快。令人惊讶的是,我会假设列表的流是有序的并且不能是无序的和/或parallelStream() 应该更快给定足够的流大小。
    • 有序属性是为您着想的功能,因此您可以获得,例如将最终结果收集到列表中时,以正确的顺序生成结果列表。如果您不关心订单,您可以随时释放该合同以允许潜在的性能提升,请参阅stackoverflow.com/a/29218074。在distinct() 的情况下,它可能会促使决定使用LinkedHashSetHashSet,这可能会影响性能,尽管在我的环境中,对顺序流的影响很小甚至不明显。令我惊讶的是,count() 不会自动暗示 .unordered()
    • 这是一个常见的错误假设,即始终存在parallelStream() 会更快的大小。有些问题根本无法很好地并行化。并行distinct() 意味着每个线程在合并之前收集到自己的集合中,基本上对剩余元素重复不同的操作。取决于可能比单个顺序运行更昂贵的元素分布。与剩余元素的数量相比,如果存在大量重复项,它可以受益。要判断这一点,您需要事先预测结果……
    【解决方案2】:

    您可以通过不使用流来更简洁地实现这一点:

    HashSet<String> loaded = new HashSet<>();
    for (String i : words) {
      loaded.add(i);
    }
    return loaded.size();
    

    你并没有从并行流中获得太多好处,因为你已经让这个循环被串行执行了。

    这种方法也是 O(n)。


    正如@Holger 所指出的,如果WordStreamCollection(而不仅仅是一个Iterable),它可以更简洁地实现:

    return new HashSet<>(words).size();
    

    但是,问题中没有具体说明WordStream是否实际上是Collection

    【讨论】:

    • 是的,如果WordStream也是Collection(至少我们从代码中知道它必须是Iterable),它可以简化为return new HashSet&lt;&gt;(words).size();...
    • @Holger 我也想这样写。但是,我怀疑它更可能是一个普通的Iterable&lt;String&gt;,因为它被称为“流”,所以就这样保留它。
    猜你喜欢
    • 2023-03-27
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-01
    • 2021-04-30
    • 2015-06-12
    • 1970-01-01
    相关资源
    最近更新 更多