简体   繁体   English

在Scala中合并二叉树

[英]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. 但是,我无法想象这在Scala中会是什么样。 Here are some of my thoughts. 这是我的一些想法。 This function would go in the BinaryTree class: 此函数将放在BinaryTree类中:

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

However, reassigning to a val is not possible. 但是,无法重新分配给val 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. 我是Scala的初学者,对此我真的很固执。

It depends on what the BinaryTree class looks like, and you do not tell much about that. 这取决于BinaryTree类的外观,而您对此并没有太多的了解。 It is completely possible to have a mutable BinaryTree , where you can modify a node. 完全有可能具有可变的BinaryTree ,您可以在其中修改节点。 But here is an implementation on your algorithm with a typical immutable BinaryTree 但是这是您的算法的实现,具有典型的不可变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. 它不会修改this ,它返回的新实例BinaryTree ,并离开this不变。 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. 这并不是说昂贵的,因为所有的节点that和大部分节点的this将在两两棵树之间共享。 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. 另一方面,很有可能使左子项和右子项var而不是val并使合并合并原始树。


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. 我刚刚修复了Node.merge中的一个错字,也许这让您感到困惑。 Mabye what confuses you is that return is usually not written scala. Mabye令您感到困惑的是,回报通常不是书面的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 在合并的两种实现方式,你可以将一个返回, return thatreturn Node(left.merge... ,这将意味着同样的事情。另外, return Node(...)相同return new Node(...) 。无论如何,这是它的工作原理

Suppose the values are integers, and this is 假设值是整数, this

    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. ABC可能是单个节点,整棵树都是空的,我们不在乎,关键是2的左孩子是空的,这就是合并的地方。

that will simply be M , because what it contains does not matter. that将只是M ,因为它包含的内容无关紧要。 Let's go. 我们走吧。

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

We call merge on that : 我们称之为merge:

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 . 关键是该代码不会返回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) . 它不再是[node 7] ,而是[node 7].merge(M) So let's compute [node 7].merge(M) . 因此,让我们计算[node 7].merge(M) [node 7] is Node([node 2], 7, B) . [node 7]Node([node 2], 7, B) Let's merge M into that. 让我们将M合并到其中。

[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: 再过一次:[node2]是Node(Empty,2,A),所以:

[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 . Empty.merge(M)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. M在结果中小于2。

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. 现在我们知道[node2] .merge(M)的含义了。 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 : 我们有[node 7] .merge(m),所以我们可以计算最终结果:

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

We know what [node 7].merge(m) is, so the final result, as intended : 我们知道[node 7].merge(m)是什么,所以最终结果如预期的那样:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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