《剑指offer》面试题26:树的子结构

判断二叉树子结构
本文介绍了一种算法,用于判断一棵二叉树是否包含另一棵二叉树作为其子结构。通过递归方法,在主树中寻找与子树根节点值相同的节点,并验证其结构是否匹配。文章提供了详细的实现思路及Java代码示例。

题目:输入两棵二叉树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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值