26 树的子结构(第3章 高质量的代码-代码的鲁棒性)

题目描述:

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

测试用例:  

 功能测试(树AB都是普通的二叉树;树B是或不是树A的子结构)

特殊输入测试(两颗树的一个或两个的根节点为nullptr;二叉树的所有节点都没有左子树或者右子树

解题思路:

1)使用递归调用

分成两步:第一步,在树A中找到与树B的根节点值一样的节点R(实质上是树的遍历);第二步,判断树A中以R为跟节点的子树是不是包含和树B一样的结构(终止条件是到达树A(不含树B结构)或树B的叶节点(含有树B结构)) 

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        bool result = false;
        if(pRoot1!=nullptr && pRoot2!=nullptr){
            if(pRoot1->val == pRoot2->val)
                //两个树的根节点相等,判断子树是否相等
                result = DoesTree1HaveTree2(pRoot1,pRoot2);
            if(!result)
                //在左子树中查找
                result = HasSubtree( pRoot1->left,  pRoot2);
            if(!result)
                //在右子树中查找
                result = HasSubtree( pRoot1->right,  pRoot2);
        }
        
        return result;
    }
    
    bool DoesTree1HaveTree2(TreeNode* pRoot1, TreeNode* pRoot2){
        //根节点相等才会进入到该函数
        //给出终止条件
        if(pRoot2==nullptr)
            return true;
        if(pRoot1==nullptr)
            return false;
        
        if(pRoot1->val != pRoot2->val)
            return false;
        //左右子树都相等,返回true
        return DoesTree1HaveTree2(pRoot1->left,pRoot2->left) && DoesTree1HaveTree2(pRoot1->right,pRoot2->right);
    }
};  

编程时注意对nullptr的判断,以防代码报错。

2)利用好短路特性,完全不用那么多flag

class Solution {
    bool isSubtree(TreeNode* pRootA, TreeNode* pRootB) {
        if (pRootB == NULL) return true;
        if (pRootA == NULL) return false;
        if (pRootB->val == pRootA->val) {
            return isSubtree(pRootA->left, pRootB->left)
                && isSubtree(pRootA->right, pRootB->right);
        } else return false;
    }
public:
    bool HasSubtree(TreeNode* pRootA, TreeNode* pRootB)
    {
        if (pRootA == NULL || pRootB == NULL) return false;
        return isSubtree(pRootA, pRootB) ||
            HasSubtree(pRootA->left, pRootB) ||
            HasSubtree(pRootA->right, pRootB);
    }
};

3)  KMP

左边数先序遍历是:1 2 4 5 3,右边是:1 2 3

子树和子结构是两码事,子树的意思是给定一个节点就要遍历到它的所有叶节点为止,而子结构不需要,可以中途停止。子树能用序列化+KMP,子结构不能,因为子结构不管采用何种方式序列化,都不能保证在整棵树的序列化中连续

可以用KMP吧?都先序遍历,如果后者是前者的子结构,则后者的先序遍历序列一定是前者的先序遍历的子串(注意是子串,不是子序列),反之,如果不是子结构,B的固定先序有多种结构,但是一定不是前者的子串了。

学习KMP算法,怎样寻找子串

4)将树进行序列化的时候,空结点要用#号或其他符号填充,这样可以保证这个序列能唯一的表示一棵树,如后面有一个题目《树的序列化和反序列化》也是这样做的。

基础知识:

1)深度优先遍历(DFS)Depth First Search 

和树的先序遍历比较类似。

2)广度优先搜索算法(Breadth First Search),又称为"宽度优先搜索"或"横向优先搜索",简称BFS。

https://www.cnblogs.com/skywang12345/p/3711483.html 

3)KMP算法要解决的问题就是在字符串(也叫主串)中的模式(pattern)定位问题。说简单点就是我们平时常说的关键字搜索。模式串就是关键字(接下来称它为P),如果它在一个主串(接下来称为T)中出现,就返回它的具体位置,否则返回-1(常用手段)。

4)kmp时间复杂度是O(n + m), 递归查找O(n * m)

5)一种遍历可能有多种树,两种遍历才能确定一个树,

猜你喜欢

转载自www.cnblogs.com/GuoXinxin/p/10449529.html