题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
一道蛮有意思的题,一开始理解错题意了,只考虑了B树完全覆盖了A中某棵子树,并且以为一定存在根节点相同就完全匹配B树的子树。然后想着只要从A树根节点往下遍历直到找到一个节点与B的根节点匹配即可。。。。还是想的太简单。
正确的题意是,再给出的A树中,任意一个部分,一段子树,该子树不一定延伸到叶子节点,这段子树与B树完全匹配,那么就不止是根节点的匹配,找到匹配的根节点后再继续使得两棵树的指针同时下移,一个一个比较该子树的每个节点是否都与B树相同
有一个不同即放弃以该根节点形成的子树。
如图所示,A树的根节点8是与B树根节点相同的,但其子树并不完全与B树相同,再者,A树左子树中有一部分与B树完全相同,但是并不是整颗子树完全相同,仅仅是一部分,那么就要从中挑出来与B树进行一一比对。
首先想到的思路即,递归查找A树中是否有某个节点的值与B树根节点一样,若一样,则认为该节点形成的子树可能包含了B,于是进入judge函数中,同时移动两棵子树上的指针,一一判断左右儿子的节点是否相同,相同则继续比较,不同直径返回false,另找其他的可能匹配的根节点。
如何判断其完全匹配,对于正在两棵树上同时移动的指针来说,若B指向的节点不存在了,那么说明B已经遍历完了该条路径上的所有节点,并且没有与A指向节点不同的值。可以返回true,表示该路径匹配。如果A指向的节点不存在了,而B指向的节点存在,那么直接返回false,因为这种情况明显是B有值而A没值,匹配不成功,第三种情况就是两者都有值,比较值是否相等即可。如果相等,同时再向左右儿子扩展匹配。
代码如下:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool judge(TreeNode* root1,TreeNode* root2)
{
if(root2==NULL)return true;//B树节点不存在了,比较完成,没有出错
if(root1==NULL)return false;//A树节点不存在,而B树节点存在,匹配失败
if (root1->val!=root2->val)return false;//同时存在,但值不同,匹配失败
else return judge(root1->left,root2->left)&&judge(root1->right,root2->right);//同时存在且匹配成功,那么同时向左右儿子扩展比较
}
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot1==NULL||pRoot2==NULL)return false;//题目已经说明,存在空树则不是子结构
else return judge(pRoot1,pRoot2)||HasSubtree(pRoot1->left,pRoot2)||HasSubtree(pRoot1->right,pRoot2);//这里三个函数都要调用,不能因为判断了某个节点值相等就只调用一个judge函数,而不调用扩展函数,这样会导致匹配失败一次即全盘判定失败
}
};