2个二叉树是否相等
可能的重复:
判断两个二叉树是否相等
昨天面试,一道题明白了,这是:
描述
有
2个二叉树
,检查它们是否相等。当且仅当
tree1->child == tree2->child
时它们相等,并且一个 树的左右子节点可以互相交换
。
例如:
5 6
/ \ / \ they are equal.
1 2 2 1
5 6
/ \ / \ they are equal.
1 2 2 1
/ \ / /
3 4 4 3
任何想法都会受到赞赏。
Possible Duplicate:
Determine if two binary trees are equal
Got an interview yesterday, a question got me, here it is:
Description
There are
2 binary trees
, check if they are equal.They are equal if and only if
tree1->child == tree2->child
, and one
tree's left and rightchildren can be swapped with each other
.
For example:
5 6
/ \ / \ they are equal.
1 2 2 1
5 6
/ \ / \ they are equal.
1 2 2 1
/ \ / /
3 4 4 3
Any ideas are appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
等式运算符具有传递性:如果 A=B,且 B=C,则 A=B=C,因此 A=C。
相等运算符是自反的:A=A、B=B 和 C=C,无论它们的值是什么。
等式运算符是对称的。如果A=B,则B=A。 (它们的顺序并不重要。)
现在,看一下他们给您的定义:
如果子树相等,则一棵树与另一棵树相等。让我们来看看。我们可以假设节点是在底部进行比较的,否则这个定义就毫无用处。但他们懒得告诉你如何解决这种比较,他们给你的整个定义取决于它。
简而言之,这是一个蹩脚的问题。
不过,让我们看看如果我们决定尝试解决这个问题会发生什么。
但是等等,他们还告诉您任何树的两个孩子都可以交换。这增加了这样的约束:任何与其他任何东西(包括其自身)相同的树都必须等于其镜像。其子树的子树的任何变体都被交换。
请记住,这应该是一个搜索树。因此,我们可以假设由相同算法处理的两个不同的搜索树如果相等,则必须给出相同的结果。因此,如果我们交换树的元素,那么搜索时间就会受到影响。因此,没有每个节点都到位的树彼此不相等。
将其与该等式的“可交换”属性放在一起,我们可以看到这不是一个有效的等式定义。 (如果我们尝试应用它,那么事实证明,只有特定级别的每个节点都具有相同节点的树才相等,并且仅对它们自己而言,这打破了相等运算符的自反性部分。)
Equality operators are transitive: If A=B, and B=C, then A=B=C so A=C.
Equality operators are reflexive: A=A, B=B, and C=C no matter what their values are.
Equality operators are symmetric. If A=B, then B=A. (It doesn't matter what order they are in.)
Now, taking a look at the definition they gave you:
A tree is equal to another tree if the children are equal. Let's see. We can assume that the nodes are being compared at the bottom, or else the definition is pretty useless. But they don't bother to tell you how to resolve that comparison, and the whole definition they gave you hinges on it.
In short, it's a crappy question.
Let's see what happens if we decide we want to try and unravel the question, though.
But wait, they also tell you that the two children of any tree can be swapped. This adds the constraint that any tree that is equal to anything else (including itself) must be equal to its mirror image. And any variations of its subtrees' children being swapped.
And remember that this is supposed to be a search tree. Therefore, we can probably assume that two different search trees that are processed by the same algorithm must give the same result if they are equal. So, if we switch around the elements of a tree, then the search time would be affected. So, trees that do not have every node in place are not equal to each other.
Putting that together with the "swappable" property of this equality, we can see it's not a valid definition of equality. (If we try to apply it, then it turns out that only trees that have the same node for every node at a particular level are equal, and only to themselves, which breaks the reflexivity part of an equality operator.)
我不认为这是一个无理的问题。一种简单的递归解决方案是
这可能非常昂贵,例如,当我们有两棵形状相似的大树时,其中所有非叶节点都具有相同的关联值,并且其中一棵树的叶节点是叶节点的排列另一个。
为了解决这个问题,您可以首先根据需要更改 left 和 right ,以便 left <对,对于 < 的一些递归定义。这也可能很昂贵,但比检查每个排列要便宜得多,而且我认为选择 << 的定义。会有帮助的。这将允许您检查与普通定义的相等性。
http://en.wikipedia.org/wiki/Canonicalization 的概念也遵循普通的平等解决有关是否确实存在等价关系的问题。等价关系相当于划分。普通的平等显然是一种划分。如果通过比较 f(x) 和 f(y) 以及等价关系来比较 x 和 y,则您将得到 x 和 y 的划分,从而得到等价关系。
进一步思考这一点,我认为使规范化或相等测试相当有效的方法是自下而上工作,用一个标记注释每个节点,该标记的值反映与其他节点比较的结果,以便您可以比较节点,以及它们下面的子树,只是比较标记。
因此,平等的第一步是使用哈希表用标记来注释每个叶子,只有当叶子上的值相等时,这些标记才相等。然后,对于其唯一子节点是叶子的节点,使用例如哈希表来分配进一步的标记,使得仅当这些节点下方的叶子(如果有)匹配时,这些节点中的标记才相等。然后您可以再上一步,这一次您可以比较子节点处的标记,而不是向下递归树。以这种方式分配代币的成本应该与所涉及的树的大小成线性关系。在顶部,您可以通过比较根处的标记来比较树。
I don't think this is an unreasonable question. A simple recursive solution is
This can be very expensive, for example in the case when we have two large trees of similar shape where all the non-leaf nodes have the same associated value and the leaf nodes of one are a permutation of the leaf nodes of another.
To get past this you could first of all change left and right as required so that left < right, for some recursive definition of <. This could also be expensive, but much less so than checking every permutation, and I think a choice of definition of < would help. This would then allow you to check for equality with an ordinary definition.
This notion of http://en.wikipedia.org/wiki/Canonicalization followed by ordinary equality also resolves questions about whether you really do have an equivalence relation. An equivalence relation is equivalent to a partition. Ordinary equality is obviously a partition. If you compare x and y by comparing f(x) and f(y) followed by an equivalence relation you have a partition of x and y, and therefore an equivalence relation.
Thinking more about this, I think the way to make either canonicalisation or equality-testing reasonably efficient is to work from the bottom up, annotating each node with a token whose value reflects the result of comparisons with other nodes, so that you can compare nodes, and the subtrees below them, just be comparing tokens.
So the first step for equality is to e.g. use a hash table to annotate each leaf with tokens which are equal only when the values at the leaves are equal. Then, for nodes whose only children are leaves, use e.g. a hash table to assign further tokens so that the tokens in those nodes are equal only when the leaves, if any, beneath those nodes match. Then you can go one more step up, and this time you can compare tokens at child nodes instead of recursing down the tree there. The cost of assigning tokens in this way should be linear in the size of the trees involved. At the top you can compare trees just by comparing the tokens at the root.
如果你用翻转不变性来实现他们对“平等”的定义,你将违反平等的定义。这个定义甚至没有意义,因为二叉搜索树不是相等的(除非每个节点都有一个指针,指向哪个子树“更大”,哪个子树“更小”)。
您有两种合理的定义选择:
拓扑(与翻转无关)等价(在这种情况下,您不能将其称为“二叉搜索树”,因为它未排序):
tree1==tree2
表示set(tree1.children)==set(tree2.children)
普通搜索树(翻转关怀)等价:
tree1==tree2
表示list(tree1.children)==list(tree2.children)
对于二叉树,上述定义将以任何支持 list 和 set 数据类型的语言按原样工作(但是 python 集在不可散列的数据类型上会阻塞)。尽管如此,下面是一些更冗长和丑陋的类似 C/Java 的定义:
拓扑等效:
t1==t2
表示(t1.left==t2.left and t1.right==t2.right) 或 (t1.left==t2.right and t1.右==t2.左)
排序树等价:
t1==t2
表示(t1.left==t2.left and t1.right==t2.right)
上面的定义是递归的;也就是说,他们假设已经为子树和基本情况定义了相等性,确实如此。
旁注:
这不是一个有效的语句,因为树节点没有单个子节点。
If you implement their definition of "equality" with flip-invariance, you will violate the definition of equality. The definition doesn't even make sense, because that's not how binary search trees are equal (unless each node has a pointer to which subtree is "greater" and which is "lesser").
You have two choices of reasonable definitions:
topological (flip-agnostic) equivalence (in which case you can't call it a "binary search tree" because it's not sorted):
tree1==tree2
meansset(tree1.children)==set(tree2.children)
normal search tree (flip-caring) equivalence:
tree1==tree2
meanslist(tree1.children)==list(tree2.children)
For binary trees, the above definitions will work as-written in any language which supports the
list
andset
datatypes (python sets will choke however on unhashable datatypes). Nevertheless, below are some more verbose and ugly C/Java-like definitions:topological equivalence:
t1==t2
means(t1.left==t2.left and t1.right==t2.right) or (t1.left==t2.right and t1.right==t2.left)
sorted tree equivalence:
t1==t2
means(t1.left==t2.left and t1.right==t2.right)
The definitions above are recursive; that is, they assume equality has been defined for the subtrees and base-cases already, which it has.
sidenote:
This is not a valid statement, because a tree node does not have a single child.
使用 @mcdowella 建议的规范化方法比较树。不同之处在于,我的方法不需要
O(N)
额外的内存(关于树中节点的数量):canonwalk()
需要O(N*M )
步骤和O(log(N)*M)
内存来生成树中的所有节点,其中N
是节点总数,每个节点有 M 个子节点(对于二进制来说是 2树)。
canonorder() 可以很容易地推广到任何节点表示和任意数量的子节点。 canonwalk() 仅要求树可以将其直接子节点作为序列访问。
调用
canonwalk()
的比较函数:示例
输出
Compare trees using canonization approach suggested by @mcdowella. The difference is that my approach doesn't require
O(N)
additional memory w.r.t number of nodes in the tree:canonwalk()
requiresO(N*M)
steps andO(log(N)*M)
memory to yield all nodes in a tree, whereN
is total number of nodes,M
number of children each node has (it is 2 for binary trees).canonorder()
could be easily generalized for any node representation and any number of children.canonwalk()
requires only that a tree can access its immediate children as a sequence.The comparison function that calls
canonwalk()
:Example
Output
我将问题读为:给定两个二叉树,对于树中的每个深度,找出它们的子集是否相互覆盖。
这可以相对容易地编码。
I read the questions as: given two binary trees, for each depth in the tree, find out whether their children's set are covered in each other.
This can be coded relatively easy.
Ruby 中无递归的解决方案
Ruby 语法提示:
arr <<元素
;在本例中,for_check
是数组的数组t1,t2 = [item1, item2]
。与arr = [item1, item2]相同; t1 = arr[0]; t2 = arr[1]
t1_children == t2_children
假设此类对象具有 == 的相应行为。更详细的是t1_children.map { |el| el.val } == t2_children.map { |el| el.val }
- 这里map
生成 vals 数组。Solution without recursion in Ruby
Ruby syntax tips:
arr << elem
; in this casefor_check
is array of arrayst1,t2 = [item1, item2]
. Same asarr = [item1, item2]; t1 = arr[0]; t2 = arr[1]
t1_children == t2_children
assumed corresponding behavior of == for this kind of objects. More verbose will bet1_children.map { |el| el.val } == t2_children.map { |el| el.val }
- heremap
produces array of vals.