剑指 Offer 26. 树的子结构

一、题目

题目链接:力扣

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

B是A的子结构, 即A中有出现和B相同的结构节点值。

例如:
给定的树 A:

      3
     / \
    4   5
   / \
 1   2

给定的树 B:

    4 
   /
 1

返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false

示例 2:

输入:A = [3,4,5,1,2], B = [4,1]
输出:true

限制:

0 <= 节点个数 <= 10000


二、题解

1、思路

🐰 深度优先+递归

递归中存在递归!(先序遍历递归中存在递归子树判断)

  1. 首先是DFS(深度优先遍历)找到A中值B根节点值(是值,不是结点)一样的结点C
  2. 找到后判断B是否为C的子树(必须包含C的根节点),此时由于B与C的根节点一样,我们就很好判断
  3. 如果满足子子树的条件,我们就立刻返回
  4. 不满足的话,因为A中值与B根节点值相等的结点可能有多个,所以继续dfsA中的其他结点

如果用一句话概括本题的思路:

如果A在根节点处与B不相同,则继续查找A的左子树或右子树,判断和B树是否有相同的子结构!!!
 

2、代码实现

🐰 深度优先+递归

class Solution{
public:
    // 查找A中以B为根节点的子树是否和B相等(递归判断是否相等)
    // B第一次进入一定不为空
    bool recur(TreeNode* A, TreeNode* B)
    {
        if(B == NULL)return true;// B为空说明B已经完成递归匹配,确定为子结构
        // A为空说明,查找所有A左或是A右都没找到
        // AB的值不同也说明不是子结构
        if(A == NULL || A->val != B->val)return false;
        return recur(A->left, B->left) && recur(A->right, B->right);
    }

    // 这个函数对这棵树进行前序遍历:
    // 即处理根节点,再递归左子节点,再递归处理右子节点
    // 特殊情况是:当A或B是空树的时候 返回false
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        // B为空,空树不是任一树的子树,直接返回flase
        // A为空,无论B是否为空,B都不会是子树,返回false
        if(A == NULL || B == NULL)return false;

        // A中找到与B根节点相等的结点,开始判断B子树是否相等
        
        // B为A的子结构有3种情况,满足任意一种即可:
        // 1.B的子结构起点为A的根节点,此时结果为recur(A,B)
        // 2.B的子结构起点隐藏在A的左子树中,而不是直接为A的根节点,此时结果为isSubStructure(A.left, B)
        // 3.B的子结构起点隐藏在A的右子树中,此时结果为isSubStructure(A.right, B)
        //bool status1 = recur(A, B);
        // 对左右子数进行判断
        //bool status2 = isSubStructure(A->left, B);
        //bool status3 = isSubStructure(A->right, B);
        //return status1 || status2 || status3;

        // 使用这一句替代上方几句,短路运算,更快
        // 这里的本质是:
        // return recure(A,B) || (A->left,B) || (A->right,B) || ... || recur(A所有结点,B)
        return recur(A, B) || isSubStructure(A->left, B)|| isSubStructure(A->right, B);
    }
};

3、复杂度分析

🐰 深度优先+递归

时间复杂度 O(MN) :其中 M,N 分别为树 A 和 树 B 的节点数量;先序遍历树 A 占用 O(M) ,每次调用 recur(A, B) 判断占用 O(N) 。

空间复杂度 O(M): 当树 A和树 B 都退化为链表时,递归调用深度最大。当 M≤N 时,遍历树 A 与递归判断的总递归深度为 M ;当 M>N 时,最差情况为遍历至树 A 叶子节点,此时总递归深度为 M。

注1:递归函数空间复杂度与系统堆栈有关,系统栈需要记住每个节点的值,我自己理解的递归一次空间复杂度+O(1)。

注2:二叉树先序遍历时间复杂度和空间复杂度为O(n)。二叉树的前序遍历、中序遍历、后序遍历、层序遍历的时间复杂度和空间复杂度

4、运行结果

🐰 深度优先+递归

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kashine

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值