剑指 Offer 26. 树的子结构

文章目录

    • 题目
    • 题解
    • 同类题解决方案

剑指 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

JAVA版本答案:

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        return (A != null && B != null) && (recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
    }
    boolean recur(TreeNode A, TreeNode B) {
    //递归结束条件1:A的一个节点B的对应位置没有,可以认为是子结构
        if(B == null) return true;
    //递归结束条件2:B的一个节点A的对应位置没有 
    // A,B对应位置节点值不同,此时必然不可能是子结构
        if(A == null || A.val != B.val) return false;
    //返回值:继续在对应位置递归判断
        return recur(A.left, B.left) && recur(A.right, B.right);
    }
}


题解

子结构和子树的区别:
子树的意思是包含了一个结点,就得包含这个结点下的所有节点,一棵大小为n的二叉树有n个子树,就是分别以每个结点为根的子树。子结构的意思是包含了一个结点,可以只取左子树或者右子树,或者都不取。

核心思想: 判断树 B 是否是树A 的子结构,其核心是这三个判断条件:

  1. 以 节点 A 为根节点的子树 包含树 B ,对应 recur(A, B);
  2. 树 B 是 树 A 左子树 的子结构,对应 isSubStructure(A.left, B);
  3. 树 B 是 树 A 右子树 的子结构,对应 isSubStructure(A.right, B);

以上 2. 3. 实质上是在对树 A 做 先序遍历
所以

1. 先序遍历树 A 中的每个节点 nA
​2. 判断树 A 中 以 nA为根节点的子树 是否包含树 B 

复杂度分析:
时间复杂度 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。

同类题解决方案

力扣上很多树的题目都是可以用递归很快地解决的,而这一系列递归解法中蕴含了一种很强大的递归思维:对称性递归(symmetric recursion)
什么是对称性递归?就是对一个对称的数据结构(这里指二叉树)从整体的对称性思考,把大问题分解成子问题进行递归,即不是单独考虑一部分(比如树的左子树),而是同时考虑对称的两部分(左右子树),从而写出对称性的递归代码(这里借用了这位大佬的解题模板:解题模板)

题型分类:
可以用对称性递归解决的二叉树问题大多是判断性问题(bool类型函数),这一类问题又可以分为以下两类:
1、不需要构造辅助函数。这一类题目有两种情况:第一种是单树问题,且不需要用到子树的某一部分(比如根节点左子树的右子树),只要利用根节点左右子树的对称性即可进行递归。第二种是双树问题,即本身题目要求比较两棵树,那么不需要构造新函数

2、需要构造辅助函数。这类题目通常只用根节点子树对称性无法完全解决问题,必须要用到子树的某一部分进行递归,即要调用辅助函数比较两个部分子树。形式上主函数参数列表只有一个根节点,辅助函数参数列表有两个节点。
给出二叉树对称性递归的解题模板
1、递归结束条件:特殊情况的判断
如果是单树问题,一般来说只要进行以下判断:

if(!root) return true/false;
if(!root->left) return true/false/递归函数;
if(!root->right) return true/false/递归函数;

如果是双树问题(根节点分别为p,q),一般来说进行以下判断:

if(!p && !q)return true/false;
if(!p || !q)return true/false;

当然也不完全是这些情况,比如有的需要加上节点值的判断,需要具体问题需要具体分析

2、返回值
通常对称性递归的返回值是多个条件的复合判断语句
可能是以下几种条件判断的组合:
节点非空的判断
节点值比较判断
(单树)调用根节点左右子树的递归函数进行递归判断
(双树)调用两棵树的左右子树的递归函数进行判断

你可能感兴趣的:(剑指offer,算法,java,数据结构)