理解两个Scala函数的效率差异的问题-递归联合

oxiaedzo  于 5个月前  发布在  Scala
关注(0)|答案(1)|浏览(52)

我正在上Scala的课程,我不明白两个函数之间的效率差异。这门课程是关于二叉搜索树的,它包含被描述为TweetSets的tweet,特别是union函数,让我感到困惑。
我知道第一个方法分解了树,并通过incl函数将元素添加到树中,但我不明白为什么第二个方法更好。
一棵树可以描述为:

class NonEmpty(elem: Tweet, left: TweetSet, right: TweetSet) extends TweetSet:

字符串
这是两个联合函数:

def union(that: TweetSet): TweetSet = left.union(right).union(that).incl(elem)
def union(that: TweetSet): TweetSet = left.union(right.union(that).incl(elem))

的数据
它们都终止于空集合中的函数并集:

def union(that: TweetSet): TweetSet = that


我知道第二个函数更有效,因为它通过了测试,而第一个没有。
编辑:我应该添加一些代码来进一步解释类。我将在下面通过它们。
这是空集:

class Empty extends TweetSet:

  def union(that: TweetSet): TweetSet = that
  def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet = acc

  def mostRetweeted: Tweet = throw java.util.NoSuchElementException()
  def helper(most: Tweet) = most

  def descendingByRetweet: TweetList = Nil

  /**
   * The following methods are already implemented
   */

  def contains(tweet: Tweet): Boolean = false

  def incl(tweet: Tweet): TweetSet = NonEmpty(tweet, Empty(), Empty())

  def remove(tweet: Tweet): TweetSet = this

  def foreach(f: Tweet => Unit): Unit = ()


这就是非空:

class NonEmpty(elem: Tweet, left: TweetSet, right: TweetSet) extends TweetSet:

  def union(that: TweetSet): TweetSet = left.union(right.union(that).incl(elem))
  def filterAcc(p: Tweet => Boolean, acc: TweetSet): TweetSet =
    if p(this.elem) then left.union(right).filterAcc(p, acc.incl(this.elem))
    else left.union(right).filterAcc(p, acc)

  def mostRetweeted: Tweet = helper(this.elem)
  
  def helper(most: Tweet): Tweet = 
    if this.elem.retweets > most.retweets then left.union(right).helper(elem)
    else left.union(right).helper(most)

  
  def descendingByRetweet: TweetList = Cons(this.mostRetweeted, this.remove(this.mostRetweeted).descendingByRetweet)

  /**
   * The following methods are already implemented
   */

  def contains(x: Tweet): Boolean =
    if x.text < elem.text then
      left.contains(x)
    else if elem.text < x.text then
      right.contains(x)
    else true

  def incl(x: Tweet): TweetSet =
    if x.text < elem.text then
      NonEmpty(elem, left.incl(x), right)
    else if elem.text < x.text then
      NonEmpty(elem, left, right.incl(x))
    else
      this

  def remove(tw: Tweet): TweetSet =
    if tw.text < elem.text then
      NonEmpty(elem, left.remove(tw), right)
    else if elem.text < tw.text then
      NonEmpty(elem, left, right.remove(tw))
    else
      left.union(right)

  def foreach(f: Tweet => Unit): Unit =
    f(elem)
    left.foreach(f)
    right.foreach(f)


至于测试,第一个实现没有完成,因为它花了很长时间。我在开始时发布的第二个联盟方法立即完成。

of1yzvn4

of1yzvn41#

在没有看到完整代码的情况下很难完全理解。但是,我认为你基本上是正确的,在这两种情况下树都被完全分解了。
我将使用一个简单的例子来实现这一点,并写出递归。尝试TweetSet A,其中A.left = B和A.right = C,然后是一个空的TweetSet S。(我将让A.elem = 'a',B.elem = 'b'等)
然后,我们评估A.union(S)的第一个和第二个算法。

A.union(S)
= B.union(C).union(S).incl('a')
= 0.union(0).union(C).incl('b').union(S).incl('a')
= C.incl('b').union(S).incl('a')

字符串
我承认,我不是100%确定如何评估它,因为我们没有TweetSet incl函数的代码。但我们可以看到的是,我们现在必须在包含和联合之间交替。对于较大的树,这将变得夸张。
第二个算法

A.union(S) 
= B.union(C.union(S).incl('a'))
= 0.union(0.union(C.union(S).incl('a')).incl('b'))
= C.union(S).incl('a').incl('b')
= 0.union(0.union(S).incl('a').incl('b').incl('c'))
= S.incl('a').incl('b').incl('c')


仅从算法语句中很难看出,但是这个递归将所有的联合放在前面,在那里它们可以被计算,而将包含物堆叠在最后。
通常,写出一个小例子(或几个)将有助于理解为什么代码会给出不同的性能,甚至不同的答案。

相关问题