简体   繁体   中英

Maximum element in a tree

I have the following ADT implementation in Scala.

How to find the maximum element in the tree? Can I introduce some helper function, and if yes, then how?

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
}

I found a solution in Haskell which feels quite intuitive can I convert it to Scala somehow?

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]

Update

I am not interested in copying the Haskell code in Scala instead I think Haskell version is more intuitive but because of this keyword and other stuff in Object oriented language. How can I write the equivalent code in object oriented style without pattern matching?

Full disclosure, still learning Scala myself, but here is two versions I came up with (which the pattern match looks like a fair translation of the Haskell code)

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)
    }
  }
}

Tests (all passing)

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)

If you don't want to use pattern matching, you will need to implement an isEmpty operation or its equivalent, to avoid calling max on an empty set.

The important thing is how the tree is organized. Based on the implementation of contains , it looks like you have an ordered tree (a "binary search tree") where every element in the left part is less than or equal to every element in the right part. If that's the case, your problem is fairly simple. Either the right sub tree is empty and the current element is the max, or the max element of the tree is the max of the right sub tree. That should be a simple recursive implementation with nothing fancy required.

Your tree is heterogeneous, which means that each node can be either a full node with a value, or an empty leaf. Hence you need to distinguish which is which, otherwise you can call max on an empty node. There are many ways:

Classic OOP:

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

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

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

So you do something like this:

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

Since JVM has class information at runtime you can skip the definition of 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 is not required if you defined max in MySet , but this pattern covers the case when you didn't)

Well, Scala has a syntactic sugar for the latter, and not surprisingly it's the pattern matching:

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

You can take it slightly further and write something like this:

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)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM