题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
二叉树的节点定义如下:
struct BinaryTreeNode
{
double value;
BinaryTreeNode* left;
BinaryTreeNode* right;
}
思路:
1.第一步在树A中查找与B根节点的值一样的节点,这实际上就是树的遍历。一般使用递归的方法去遍历。
2.第二步是判断树A中以R为根节点的子树是不是和树B具有相同的结构。递归的方法:如果节点R的值和树B的根节点不相同,则以R为根节点的子树和树B肯定不具有相同的节点;如果它们的值相同,则递归地判断它们各自的左右节点的值是不是相同。递归的终止条件是我们达到了树A或者树B的叶节点。
3.注意一个细节,由于计算机表示小数(包括float和double型小数)都有误差,我们不能直接用等号(==)判断两个小数是否相等。如果两个小数的差的绝对值很小,就可以认为他们相等。
java参考代码如下:
package chapter3;
public class P148_SubstructureInTree {
public static class TreeNode{
int val;
TreeNode left;
TreeNode right;
TreeNode(int val){
this.val=val;
this.left=null;
this.right=null;
}
}
public static boolean hasSubtree(TreeNode root1, TreeNode root2){
boolean result=false;
if(root1!=null&&root2!=null){
if(root1.val==root2.val)
result=tree1HasTree2FromRoot(root1,root2);
if(!result)//上述没有找到
result=hasSubtree(root1.left,root2);
if(!result)//左子树没有找到
result=hasSubtree(root1.right,root2);
}
return result;
}
public static boolean tree1HasTree2FromRoot(TreeNode root1, TreeNode root2){
if(root2==null)
return true;
if(root1==null)
return false;
if(root1.val!=root2.val)
return false;
return tree1HasTree2FromRoot(root1.left,root2.left)&&tree1HasTree2FromRoot(root1.right,root2.right);
}
//[LeetCode NO.572] Subtree of Another Tree 另一个树的子树
public static boolean isSameTree(TreeNode root1,TreeNode root2){
if(root1==null&&root2==null) return true;
if((root1==null&&root2!=null)||(root1!=null&&root2==null)||(root1.val!=root2.val)) return false;
return isSameTree(root1.left,root2.left)&&isSameTree(root1.right,root2.right);
}
public static boolean isSubtree(TreeNode root1,TreeNode root2){
if(root1==null) return false;
boolean result=false;
result=isSameTree(root1,root2);
if(!result)
result=isSubtree(root1.left,root2);
if(!result)
result=isSubtree(root1.right,root2);
return result;
}
public static void main(String[] args){
TreeNode root1 = new TreeNode(8);
root1.left = new TreeNode(8);
root1.right = new TreeNode(7);
root1.left.left = new TreeNode(9);
root1.left.right = new TreeNode(2);
root1.left.right.left = new TreeNode(4);
root1.left.right.right = new TreeNode(7);
TreeNode root2 = new TreeNode(8);
root2.left = new TreeNode(9);
root2.right = new TreeNode(2);
TreeNode root3 = new TreeNode(2);
root3.left = new TreeNode(4);
root3.right = new TreeNode(3);
TreeNode root4 = new TreeNode(2);
root4.left = new TreeNode(4);
root4.right = new TreeNode(7);
System.out.println(hasSubtree(root1,root2));
System.out.println(hasSubtree(root1,root3));
System.out.println(isSubtree(root1,root4));
System.out.println(isSameTree(root3,root2));
System.out.println(isSameTree(root3,root4));
}
}
注意:
上题的相关题:让我们求一个数是否是另一个树的子树,从题目中的第二个例子中可以看出,子树必须是从叶结点开始的,中间某个部分的不能算是子树,那么我们转换一下思路,是不是从s的某个结点开始,跟t的所有结构都一样,那么问题就转换成了判断两棵树是否相同,也就是Same Tree的问题了,这点想通了其实代码就很好写了,用递归来写十分的简洁,我们先从s的根结点开始,跟t比较,如果两棵树完全相同,那么返回true,否则就分别对s的左子结点和右子结点调用递归再次来判断是否相同,只要有一个返回true了,就表示可以找得到。
转自:http://www.cnblogs.com/grandyang/p/6828687.html
基于以上思路,参考代码如下:
// 注意:double在计算机中的比较是有误差的,需要给定允许的误差范围
bool equal(double n1, double n2)
{
if(n1 - n2 > -0.0000001 && n1-n2 < 0.0000001)
return true;
else
return false;
}
bool doesTree1hasTree2(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
if(pRoot2 == nullptr)
return true;
if(pRoot1 == nullptr)
return false;
if(!equal(pRoot1->value, pRoot2->value))
return false;
return doesTree1hasTree2(pRoot1->left, pRoot2->left) &&
doesTree1hasTree2(pRoot1->right, pRoot2->right);
}
// 先根据B的根节点,找到在A中对应的结点,再进行其他剩余结点的比较
bool hasSubtree(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
bool result = false;
if(pRoot1 != nullptr && pRoot2 != nullptr)
{
// 根节点值相等,开始比较其他结点
if(equal(pRoot1->value, pRoot2->value))
result = doesTree1hasTree2(pRoot1, pRoot2);
if(!result)
result = doesTree1hasTree2(pRoot1->left, pRoot2);//尝试A树的左子树
if(!result)
result = doesTree1hasTree2(pRoot1->right, pRoot2);//尝试A树的右子树
}
return result;
}
测试用例:
a.功能测试(树A和树B都是普通的二叉树;树B是或者不是树A的子结构)。
b.特殊输入测试(两棵二叉树的一个或者两个根节点为nullptr指针;二叉树的所有节点都没有左子树或者右子树)。
参考:
https://blog.youkuaiyun.com/w_bu_neng_ku/article/details/81267246