如何在Java中实现二叉树节点类和二叉树类以支持最有效(从运行时角度来看)相等检查方法(也必须实现):
boolean equal(Node<T> root1, Node<T> root2) {}
或
boolean equal(Tree t1, Tree t2) {}
首先,我按如下方式创建了Node类:
public class Node<T> {
private Node<T> left;
private Node<T> right;
private T data;
// standard getters and setters
}
然后是equals方法,它将2个根节点作为参数并运行标准的递归比较:
public boolean equals(Node<T> root1, Node<T> root2) {
boolean rootEqual = false;
boolean lEqual = false;
boolean rEqual = false;
if (root1 != null && root2 != null) {
rootEqual = root1.getData().equals(root2.getData());
if (root1.getLeft()!=null && root2.getLeft() != null) {
// compare the left
lEqual = equals(root1.getLeft(), root2.getLeft());
}
else if (root1.getLeft() == null && root2.getLeft() == null) {
lEqual = true;
}
if (root1.getRight() != null && root2.getRight() != null) {
// compare the right
rEqual = equals(root1.getRight(), root2.getRight());
}
else if (root1.getRight() == null && root2.getRight() == null) {
rEqual = true;
}
return (rootEqual && lEqual && rEqual);
}
return false;
}
我的第二次尝试是使用数组和索引来遍历遍历树。然后可以使用两个数组上的按位运算(AND)进行比较 - 从2个数组中读取块并使用逻辑AND逐个屏蔽。我没有让我的代码工作,所以我不在这里发布(我很感激你实现的第二个想法以及你的改进)。
有关如何最有效地对二叉树进行相等测试的想法吗?
修改
这个问题假设结构平等。 (不是语义相等)
然而,测试语义相等性的代码,例如“如果它们的内容相同,我们是否应该认为这两棵树是相同的,即使它们的结构不相同?”将按顺序迭代树,它应该是直截了当的。
答案 0 :(得分:29)
好吧,有一件事你总是总是检查分支,即使你发现根是不相等的。如果您在发现不平等时刚刚返回false
,那么您的代码会更简单(IMO)并且效率更高。
简化操作的另一个选择是允许equals
方法接受null
值并将两个空值相等。这样您就可以避免在不同分支中进行所有这些无效检查。这不会使它更有效率,但它会更简单:
public boolean equals(Node<T> root1, Node<T> root2) {
// Shortcut for reference equality; also handles equals(null, null)
if (root1 == root2) {
return true;
}
if (root1 == null || root2 == null) {
return false;
}
return root1.getData().equals(root2.getData()) &&
equals(root1.getLeft(), root2.getLeft()) &&
equals(root1.getRight(), root2.getRight());
}
请注意,如果root1.getData()
返回null
,目前会失败。 (对于添加节点的方式,这可能是也可能不存在。)
要么你需要使你的树不可变(这是另一个讨论)或你需要每个节点知道它的父节点,所以当节点是更改(例如通过添加叶子或更改值),它需要更新其哈希码并要求其父级更新。
答案 1 :(得分:25)
出于好奇,如果两棵树的内容相同,你认为两棵树是否相同,即使它们的结构不相同?例如,这些是否相等?
B C C A
/ \ / \ / \ \
A D B D A D B
/ / \ \
C A B C
\
D
这些树在相同的顺序中具有相同的内容,但由于结构不同,通过您的测试将不相等。
如果你想测试这个相等性,我个人只是使用顺序遍历为树构建一个迭代器,并迭代遍历树,逐个元素地比较它们。
答案 2 :(得分:21)
首先,我做了一些一般的假设。这些假设对大多数基于树的集合类都有效,但总是值得检查:
假设这些假设是正确的,那么我建议的方法是:
this
不能为null)。此外,JVM非常擅长优化静态方法。因此,我的实施将是:
public static boolean treeEquals(Node a, Node b) {
// check for reference equality and nulls
if (a == b) return true; // note this picks up case of two nulls
if (a == null) return false;
if (b == null) return false;
// check for data inequality
if (a.data != b.data) {
if ((a.data == null) || (b.data == null)) return false;
if (!(a.data.equals(b.data))) return false;
}
// recursively check branches
if (!treeEquals(a.left, b.left)) return false;
if (!treeEquals(a.right, b.right)) return false;
// we've eliminated all possibilities for non-equality, so trees must be equal
return true;
}
答案 3 :(得分:3)
对于任何树,最有效的方式来表示它,以便您可以轻松检查相等是父列表 - 保持一个数组,其中为每个顶点记住其父项的索引(实际上保持一对 - 索引为父亲和数据值)。那你简直就是 应该比较两个连续的内存块。
仅当树是静态的(即不随时间变化)时才会起作用。如果两个树中的顶点索引相同,它也只会认为树是相等的。
我相信上述两个陈述不正确的常见情况,你的实施应该尽可能快。
编辑:事实上,如果你遵循Jon Skeet的回答中的建议,你的实现可以得到改善(至少在你知道树不相等时返回false)
答案 4 :(得分:0)
上面给出的代码对于具有相同根值的两个不相等的树将返回true。我不认为这是你想要的。不应该是:
如果(!a == b)返回false;
这样,该方法将执行其余检查。
(由于某种原因无法从这里登录。)