题目:二叉树的结点定义如下:
struct BinaryTreeNode
{
int value;
TreeNode* lchild;
TreeNode* rchild;
};
输入两棵二叉树A和B,判断树B是不是A的子结构。
例如,下图中的两棵树A和B,由于A中有一部分子树的结构和B是一样的,因此B就是A的子结构。
1 8
/ \ / \
8 7 9 2
/ \
9 2
/ \
4 7
分析:这是2010年微软校园招聘时的一道题目。二叉树一直是微软面试题中经常出现的数据结构。对微软有兴趣的读者一定要重点关注二叉树。
回到这个题目的本身。要查找树A中是否存在和树B结构一样的子树,我们可以分为两步:第一步在树A中找到和B的根结点的值一样的结点N,第二步再判断树A中以N为根结点的子树是不是包括和树B一样的结构。
第一步在树A中查找与根结点的值一样的结点。这实际上就是树的遍历。对二叉树这种数据结构熟悉的读者自然知道我们可以用递归的方法去遍历,也可以用循环的方法去遍历。由于递归的代码实现比较简洁,面试时如果没有特别要求,我们通常都会采用递归的方式。下面是参考代码:
//判定一棵树是否为另一棵树的子结构
bool HasSubTree(BinaryTree *root1, BinaryTree *root2)
{
if(root1==NULL && root2!=NULL || root1!=NULL && root2==NULL)
return false;
if(root1==NULL && root2==NULL)
return true;
return HasSubTreeCore(root1, root2);
}
bool HasSubTreeCore(BinaryTree *root1, BinaryTree *root2)
{
bool result = false;
if(root1->value == root2->value)
result = DoesTree1HasAllNodesOfTree2(root1, root2);
if(!result && root1->lchild!=NULL)
result = HasSubTreeCore(root1->lchild, root2);
if(!result && root1->rchild!=NULL)
result = HasSubTreeCore(root1->rchild, root2);
return result;
}
在上述代码中,我们递归调用hasSubtreeCore遍历二叉树A。如果发现某一结点的值和树B的头结点的值相同,则调用DoesTree1HaveAllNodeOfTree2,做第二步判断。
在面试的时候,我们一定要注意边界条件的检查,即检查空指针。当树A或树B为空的时候,定义相应的输出。如果没有检查并做相应的处理,程序非常容易崩溃,这是面试时非常忌讳的事情。由于没有必要在每一次递归中做边界检查(每一次递归都做检查,增加了不必要的时间开销),上述代码只在HasSubtree中作了边界检查后,在HasSubtreeCore中作递归遍历。
接下来考虑第二步,判断以树A中以N为根结点的子树是不是和树B具有相同的结构。同样,我们也可以用递归的思路来考虑:如果结点N的值和树B的根结点不相同,则以N为根结点的子树和树B肯定不具有相同的结点;如果他们的值相同,则递归地判断他们的各自的左右结点的值是不是相同。递归的终止条件是我们到达了树A或者树B的叶结点。参考代码如下:
bool DoesTree1HasAllNodesOfTree2(BinaryTree *root1, BinaryTree *root2)
{
if(root2==NULL)
return true;
if(root1==NULL)
return false;
if(root1->value != root2->value)
return false;
return DoesTree1HasAllNodesOfTree2(root1->lchild, root2->rchild) &&
DoesTree1HasAllNodesOfTree2(root1->rchild, root2->rchild);
}