简体   繁体   中英

Merge binary trees in Scala

It says here :

The easiest way to merge two binary trees is to take iterate down the left child of one tree until reaching a node without a left child. Then add the other tree's root as the left child.

Easy. In "normal code", I'd write something like this:

if(!left.isEmpty)
  thisFuncAgain()
else left = otherSet; return this

However, I cannot imagine how this would look in Scala. Here are some of my thoughts. This function would go in the BinaryTree class:

def merg(that: BinaryTree): BinaryTree = {
  if(leftNode.isEmpty) leftNode += that; leftNode
  else merg(leftNode)
}

However, reassigning to a val is not possible. I could recurse this function for the left node but then when it returned, the elements previous to the last node would not be accounted for.

Any tips on how I could think of this? I'm a beginner in Scala and I'm really stuck at this.

It depends on what the BinaryTree class looks like, and you do not tell much about that. It is completely possible to have a mutable BinaryTree , where you can modify a node. But here is an implementation on your algorithm with a typical immutable BinaryTree

sealed trait BinaryTree[A] {
  def merge(that: BinaryTree[A]): BinaryTree[A]
}
case object Empty extends BinaryTree[Nothing] {
  def merge(that: BinaryTree[A]: BinaryTree[A] = that
}
case class Node(left: BinaryTree[A], value: A, right: BinaryTree[A]) 
extends BinaryTree[A] {
  def merge(that: BinaryTree[A]): BinaryTree[A] =
   Node(left.merge(that), value, right)
}

It does not modify this , it returns new instances of BinaryTree , and leaves this unchanged. It is not that costly, as all the nodes of that and most of the nodes of this will be shared between the two trees. The only nodes that needs to be recreated are between the root of this and its leftmost node. You gain the benefit of immutability, foremost that it is freely shareable, you never need to make a defensive copy.

On the other hand, it is quite possible to make left and right children var rather than val and to have merge mutate the original tree.


You state in comment that it just return the tree to be merged. It does not. There was a typo in Node.merge I just fixed, maybe it is what confused you. Mabye what confuses you is that return is usually not written scala. In the two implementations of merge, you may put a return, return that and return Node(left.merge... , it would mean the very same thing. Also, return Node(...) is the same as return new Node(...) . Anyway, here is how it works

Suppose the values are integers, and this is

    5
   / \
  7   C
 / \
2   B
 \
  A

A , B , C may be single nodes, whole trees, empty, we don't care, the point is that the left child of 2 is empty and this is where the merge will take place.

that will simply be M , because what it contains does not matter. Let's go.

this is Node([node 7], 5, C)

We call merge on that :

Node([node 7], 5, C).merge(M)

It returns

Node([node 7].merge(M), 5, C)

The point is that this code does not return this . It returns a new node, where the left child is not what it was in the original node. It is no longer [node 7] , it is [node 7].merge(M) . So let's compute [node 7].merge(M) . [node 7] is Node([node 2], 7, B) . Let's merge M into that.

[node 7].merge(M) = Node([node 2], 7, B).merge(M) 
                  = Node([node2].merge(M), 7, B). 

Again, in the returned node, we replace the left child. One more time: [node2] is Node(Empty, 2, A), so:

[node 2].merge(M) = Node(Empty, 2, A).merge(M) 
                  = Node(Empty.merge(M), 2, A). 

Now we get to the bottom. Empty.merge(M) is M . We just need to rewind and all will comme in place.

[node 2].merge(M) = Node(Empty, 2, A).merge(M) 
                  = Node(Empty.merge(M), 2, A) 
                  = Node(M, 2, A)

    2
   / \
  M   A

As you see, we have actually done something. M is under 2 in the result.

Another step up:

[node 7].merge(m) = Node([node 2], 7, B).merge(M) 
                  = Node([Node 2].merge(M), 7, B). 

Now we know what [node2].merge(M) means. So

Node([node 2], 7, B).merge(M) = Node(Node(M, 2, A), 7, B)

    7
   / \
  2   B
 / \
M   A

We have [node 7].merge(m), so we can compute the final result :

this.merge(m) = Node([node 7].merge(m), 5, C)

We know what [node 7].merge(m) is, so the final result, as intended :

        5
       / \
      7   C
     / \
    2   B
   / \
  M   A

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