树中的最大元素

时间:2016-08-21 08:48:51

标签: algorithm scala recursion recursive-datastructures

我在Scala中有以下ADT实现。

如何在树中找到最大元素?我可以介绍一些辅助函数,如果是,那么如何?

abstract class MySet {
  def max: Int

  def contains(tweet: Tweet): Boolean = false
}

class Empty extends MySet {
  def max: throw new NoSuchElementExeption("max called on empty tree")

  def contains(x: Int): Boolean =
    if (x < elem) left.contains(x)
    else if (elem < x) right.contains(x)
    else true
}

class Node(elem: Int, left: MySet, right: MySet) extends Set {
  def max: { ... }

  def contains(x: Int): Boolean =
    if (x < elem) left.contains(x)
    else if (elem < x) right.contains(x)
    else true
}

我在Haskell中找到了一个非常直观的解决方案,我能以某种方式将其转换为Scala吗?

data Tree a = Nil | Node a (Tree a) (Tree a)
maxElement Nil = error "maxElement called on empty tree"
maxElement (Node x Nil Nil) = x
maxElement (Node x Nil r) = max x (maxElement r)
maxElement (Node x l Nil) = max x (maxElement l)
maxElement (Node x l r) = maximum [x, maxElement l, maxElement r]

更新

我对在Scala中复制Haskell代码不感兴趣,我认为Haskell版本更直观,但由于this关键字和面向对象语言中的其他东西。如何在没有模式匹配的情况下以面向对象的方式编写等效代码?

3 个答案:

答案 0 :(得分:1)

完全披露,仍在学习Scala,但这里是我提出的两个版本(模式匹配看起来像是Haskell代码的公平翻译)

sealed trait Tree {
  def max: Int
  def maxMatch: Int
}
case object EmptyTree extends Tree {
  def max = 0
  def maxMatch = 0
}
case class Node(data:Int,
                left:Tree = EmptyTree,
                right:Tree = EmptyTree) extends Tree {

  def max:Int = {
    data
      .max(left.max)
      .max(right.max)
  }

  def maxMatch: Int = {
    this match {
      case Node(x,EmptyTree,EmptyTree) => x
      case Node(x,l:Node,EmptyTree) => x max l.maxMatch
      case Node(x,EmptyTree,r:Node) => x max r.maxMatch
      case Node(x,l:Node,r:Node) => x max (l.maxMatch max r.maxMatch)
    }
  }
}

测试(全部通过)

val simpleNode = Node(3)
assert(simpleNode.max == 3)
assert(simpleNode.maxMatch == 3)

val leftLeaf = Node(1, Node(5))
assert(leftLeaf.max == 5)
assert(leftLeaf.maxMatch == 5)


val leftLeafMaxRoot = Node(5, 
                        EmptyTree, Node(2))
assert(leftLeafMaxRoot.max == 5)
assert(leftLeafMaxRoot.maxMatch == 5)

val nestedRightTree = Node(1, 
                         EmptyTree, 
                         Node(2, 
                             EmptyTree, Node(3)))
assert(nestedRightTree.max == 3)
assert(nestedRightTree.maxMatch == 3)

val partialFullTree = Node(1, 
                         Node(2, 
                             Node(4)),
                         Node(3, 
                              Node(6, Node(7))))
assert(partialFullTree.max == 7)
assert(partialFullTree.maxMatch == 7)

答案 1 :(得分:1)

如果您不想使用模式匹配,则需要实现isEmpty操作或其等效操作,以避免在空集上调用max

重要的是树的组织方式。基于contains的实现,看起来你有一个有序树(&#34;二叉搜索树&#34;),其中左边部分的每个元素都小于或等于右边的每个元素部分。如果是这样的话,你的问题就很简单了。右子树是空的,当前元素是最大值,或者树的最大元素是右子树的最大值。这应该是一个简单的递归实现,没有任何花哨的要求。

答案 2 :(得分:1)

您的树是异构的,这意味着每个节点可以是带有值的完整节点,也可以是空叶。因此,您需要区分哪个是哪个,否则您可以在空节点上调用max。有很多方法:

经典OOP:

abstract class MySet {
  def isEmpty: Boolean
  ...
}

class Empty extends MySet {
  def isEmpty = true
  ...
}

class Node(...) extends MySet {
  def isEmpty = false
  ...
}

所以你做这样的事情:

var maxElem = elem
if(!left.isEmpty)
  maxElem = maxElem.max(left.max)
end
if(!right.isEmpty)
  maxElem = maxElem.max(right.max)
end

由于JVM在运行时具有类信息,因此您可以跳过isEmpty

的定义
var maxElem = elem
if(left.isInstanceOf[Node])
  maxElem = maxElem.max(left.asInstanceOf[Node].max)
end
if(left.isInstanceOf[Node])
  maxElem = maxElem.max(right.asInstanceOf[Node].max)
end

(如果您在asInstanceOf中定义了max,则不需要MySet,但这种模式涵盖了您没有这样做的情况

嗯,Scala有后者的语法糖,毫不奇怪它是模式匹配:

var maxElem = elem
left match {
  case node: Node =>
    maxElem = maxElem.max(node.max)
  case _ =>
}
right match {
  case node: Node =>
    maxElem = maxElem.max(node.max)
  case _ =>
}
maxElem

你可以稍微进一步写下这样的东西:

def max = (left, right) match {
  case (_: Empty, _: Empty) => elem
  case (_: Empty, node: Node)  => elem.max(node.max)
  case (node: Node, _: Empty)  => elem.max(node.max)
  case (leftNode: Node, rightNode: Node)  =>
    elem.max(leftNode.max).max(rightNode.max)
}