代码实现起来主要容易忽略两方面的问题。
第一版:
public static boolean hasSubTree(TreeNode root1,TreeNode root2){
if(root1==null||root2==null) return false;
if(root1.val!=root2.val){
return hasSubTree(root1.left,root2)||hasSubTree(root1.right,root2);
}else{
boolean hasLeft=true;
if(root2.left!=null){
hasLeft=hasSubTree(root1.left,root2.left);
}
boolean hasRight=true;
if(root2.right!=null){
hasRight=hasSubTree(root1.right,root2.right);
}
return true;
}
}
乍一看好像没什么问题,结果不能AC。仔细查看测试用例,发现忽略了root1中多次出现root2的根结点,并且子树出现在第二次或之后的情况。
第二版:
public static boolean hasSubTree(TreeNode root1,TreeNode root2){
if(root1==null||root2==null) return false;
if(root1.val!=root2.val){
return hasSubTree(root1.left,root2)||hasSubTree(root1.right,root2);
}else{
boolean hasLeft=true;
if(root2.left!=null){
hasLeft=hasSubTree(root1.left,root2.left);
if(!hasLeft){
return hasSubTree(root1.left,root2);
}
}
boolean hasRight=true;
if(root2.right!=null){
hasRight=hasSubTree(root1.right,root2.right);
if(!hasRight){
return hasSubTree(root2.right,root2);
}
}
return true;
}
}
结果还是不能AC,原因是这种判断是分解式而不是整体式的。比如root1={8,8,7,9,2},root2={8,9,2}在判断完根结点8之后,接着只会判断root1的左子树中是否含有9而不是左子结点是不是9,那么显然是含有的。继而函数只会判断root1的右子树是否包含2,而不会继续在左子树中搜索是否含有root2,从而给出错误判断。
第三版:
经过两次失败总算明白了,要不判断树是不是另一个树的子树,一定要先知道如何判断树不是另一棵树的直接子树。
public static boolean hasSubTreeDirectly(TreeNode root1,TreeNode root2){
if(root2==null) return true;
if(root1==null) return false;
if(root1.val!=root2.val) return false;
return hasSubTreeDirectly(root1.left,root2.left)&&hasSubTreeDirectly(root1.right,root2.right);
}
当遇到与root2根结点值相同的结点时,先看此结点是不是直接包含子树root2。如果结点值不相等或者不直接包含子树root2,则分别在其左右子树中继续查找。
public static boolean hasSubTree(TreeNode root1,TreeNode root2){
if(root1==null||root2==null) return false;
boolean result=false;
if(root1.val==root2.val){
result=hasSubTreeDirectly(root1,root2);
}
if(!result){
result=hasSubTree(root1.left,root2)||hasSubTree(root1.right,root2);
}
return result;
}