【问题标题】:How to find the first duplicate in a Stream in scala如何在scala的流中找到第一个重复项
【发布时间】:2016-12-31 11:45:08
【问题描述】:

如何在 scala 的 Stream 中找到第一个重复项?

我目前的想法是将每个元素与所有先前元素的Set 配对。之后,find 会在结果 Stream 上被调用。

所以,对于每个元素,我们有

  • Set 中的插入:O(1)
  • Set 中的测试contains:O(1)

因此,这个算法的整体复杂度似乎是 O(n)。

  def firstDuplicate[A](s: Stream[A]) = {
    def recurse(s: Stream[A], set: Set[A]) : Stream[(A, Set[A])]=
      (s.head, set) #:: recurse(s.tail, set + s.head)
    val pairedWithElements = recurse(s, Set.empty)
    pairedWithElements.find{ case (e, elems) => elems.contains(e)}.get._1
  }

有没有更好的办法?

【问题讨论】:

  • 您希望流有多大?更好的是,可以出现的元素有多少?
  • 这似乎是相当理想的方法,尽管对于大型或无限流它会使用大量内存。您可以尝试使用 Bloom filter 而不是 set,但您需要多次检查前 n 个元素,因为它可能会返回误报。

标签: scala stream


【解决方案1】:

你应该让你的函数尾递归。按照您的方式,您正在堆栈上制作整个流的几乎另一个副本。另外,我不明白您为什么要复制整个流(以及一大堆集),然后再次扫描以找到副本。将其添加到集合时,您可以立即判断它是一个 dup,然后就停在那里。

可能是这样的:

   def firstDup[T](s: Stream[T], seen: Set[T] = Set.empty[T]): Option[T] =  s match {
      case head #:: tail if seen(head) => Some(head)
      case head #:: tail => firstDup(tail, seen + head)
      case _ => None
   }

来自上述 cmets 的布隆过滤器建议对于真正巨大的输入流来说是个好主意。在这种情况下,“外壳”将保持不变,您只需要更改底层的 seen 实现。

【讨论】:

  • 感谢您提供更简单的解决方案。只有 2 个错别字:s/#:/#:: 和 s/empty/empty[T]
  • 第一个是错字(现已修复)。另一个不是 - 类型参数在那里是不必要的,可以推断出来。
  • 不带type参数,调用方法时遇到这个编译错误:[error] 注意:Nothing <: pos but trait set is invariant in type a.>_ <: Pos。 (SLS 3.2.10)
  • 嗯,你说得对……我没想到真的这么称呼它……奇怪的是firstDup(Seq(1,2,3,4,2).toStream, Set.empty) 确实有效。哦,好吧...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-26
  • 2016-02-03
  • 1970-01-01
  • 2018-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多