有两个储存字符数据的二叉树T1和T2,允许重复。如何判断T2是否是T1的子树?T1有数百万个节点,而T2只有数百个节点。
有两个储存字符数据的二叉树T1和T2,允许重复。如何判断T2是否是T1的子树?T1有数百万个节点,而T2只有数百个节点。
遍历T1。如果当前节点等于T2的根节点,则同时遍历这两棵树(T2和T1的当前子树)。比较当前节点。如果它们总是相等的,那么T2就是T1的子树。
我不确定我的想法是否正确。但是,供您参考。
其中一种简单的方法是为树编写is_equal()方法,然后执行以下操作:
bool contains_subtree(TNode*other) {
// optimization
if(nchildren < other->nchildren) return false;
if(height < other->height) return false;
// go for real check
return is_equal(other) || (left != NULL && left->contains_subtree(other)) || (right != NULL && right->contains_subtree(other));
}
bool is_equal(TNode*other) {
if(x != other->x) return false;
if(height != other->height) return false;
if(nchildren != other->nchildren) return false;
if(hashcode() != other->hashcode()) return false;
// do other checking for example check if the children are equal ..
}
bool contains_subtree(TNode*other) {
// optimization
if(nchildren < other->nchildren) return false;
if(height < other->height) return false;
// go for real check
if(is_equal(other)) return true;
if(left == NULL || right == NULL) {
return (left != NULL && left->contains_subtree(other)) || (right != NULL && right->contains_subtree(other));
}
if(left->nchildren < right->nchildren) { // find in smaller child tree first
return (left->contains_subtree(other)) || right->contains_subtree(other);
} else {
return (right->contains_subtree(other)) || left->contains_subtree(other);
}
}
void serialize(ostream&strm) {
strm << x << '(';
if(left)
left->serialize(strm);
strm << ',';
if(right)
right->serialize(strm);
strm << ')';
}
我们可以使用一些优化的算法,例如Knuth-Morris-Pratt算法来查找(可能在O(n)时间内)子字符串的存在,并最终确定一个树是否是另一个树的子树。
同样,字符串可以通过Burrows-Wheeler变换进行高效压缩。并且可以使用bzgrep在压缩数据中搜索子字符串。
另一种方法是按高度和子节点数量对树中的子树进行排序。
bool compare(TNode*other) {
if(height != other->height)
return height < other->height;
return nchildren < other->nchildren;
}
编辑:
只有在明确指出T2实际上是一个不同的树对象,并且我们需要找出T1中是否存在与T2相同的子树时,这种方法才行。
那么这种方法就行不通了。
我们别无选择,只能采用已经在这里讨论过的解决方案。
我假设你的树是不可变的树,因此您永远不会更改任何子树(在Scheme术语中,您不会执行set-car!
),而只是从叶子或现有树构建新树。
那么,我建议在每个节点(或子树)保留一个哈希码。用C语言来说,将tree-s声明为
struct tree_st {
const unsigned hash;
const bool isleaf;
union {
const char*leafstring; // when isleaf is true
struct { // when isleaf is false
const struct tree_st* left;
const struct tree_st* right;
};
};
};
在构建时计算哈希值,当比较节点是否相等时,首先比较它们的哈希值是否相等;大多数情况下哈希码会不同(您不需要比较内容)。
这里是一个可能的叶子节点构建函数:
struct tree_st* make_leaf (const char*string) {
assert (string != NULL);
struct tree_st* t = malloc(sizeof(struct tree_st));
if (!t) { perror("malloc"); exit(EXIT_FAILURE); };
t->hash = hash_of_string(string);
t->isleaf = true;
t->leafstring = string;
return t;
}
计算哈希码的函数是:
unsigned tree_hash(const struct tree_st *t) {
return (t==NULL)?0:t->hash;
}
sleft
和 sright
构成的节点的函数为:struct tree_st*make_node (const struct tree_st* sleft,
const struct tree_st* sright) {
struct tree_st* t = malloc(sizeof(struct tree_st));
if (!t) { perror("malloc"); exit(EXIT_FAILURE); };
/// some hashing composition, e.g.
unsigned h = (tree_hash(sleft)*313) ^ (tree_hash(sright)*617);
t->hash = h;
t->left = sleft;
t->right = sright;
return t;
}
tx
和 ty
)利用哈希码不同则比较对象不同的特点。bool equal_tree (const struct tree_st* tx, const struct tree_st* ty) {
if (tx==ty) return true;
if (tree_hash(tx) != tree_hash(ty)) return false;
if (!tx || !ty) return false;
if (tx->isleaf != ty->isleaf) return false;
if (tx->isleaf) return !strcmp(tx->leafstring, ty->leafstring);
else return equal_tree(tx->left, ty->left)
&& equal_tree(tx->right, ty->right);
}
大多数情况下,tree_hash(tx) != tree_hash(ty)
测试会成功,您不必递归。equal_tree
函数,您可以使用其他答案(或手册中提到的技术)。