​welcome to my blog​

剑指offer面试题26(java版):树的子结构

题目描述

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

第三次做; 核心: 1)区分子树和子结构, 本题是子结构! 2) 递归函数逻辑: 以A为根节点, 判断B是不是A的子结构(当前条件), 是的话返回true, 不是的话判断A的左子树中是否有B的子结构, 是的话返回true, 不是的话判断A的右子树中是否有B的子结构, 返回结果

//核心: 区分子树和子结构!
class Solution {
//递归函数逻辑: 以A为根节点, 判断B是不是A的子结构(当前条件), 是的话返回true, 如果不是, 那么以A的左孩子为根节点判断B是不是A的左孩子的子结构(新条件新递归), 是的话返回true, 如果不是, 那么以A的右孩子为根节点判断B是不是A的右孩子的子结构, 返回结果
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(B==null)
return false;
if(A==null)
return false;
boolean res = core(A, B);
if(res)
return res;
res = isSubStructure(A.left, B);
if(res)
return res;
res = isSubStructure(A.right, B);
return res;
}

//判断A和B的结构是否相等; 递归函数逻辑: 判断A.val是否等于B.val(当前条件), 相等的话再判断A的左子树是否等于B的左子树(新条件新递归), A的右子树是否等于B的右子树(新条件新递归)
private boolean core(TreeNode A, TreeNode B){
if(B==null)
return true;
if(A==null)
return false;
//here, A!=null && B!=null
if(A.val!=B.val)
return false;
boolean res = core(A.left, B.left);
if(res==false)
return false;
res = core(A.right, B.right);
return res;
}
}

笔记

  • 常规情况: 判断A的某个节点值和root2.val是否相等, 是的话判断当前节点的左节点值和root2.left.val是否相等, 当前节点的右节点值和root2.right.val是否相等. 如此递归
  • 上面过程涉及到了二叉树的遍历, 采用递归的方式实现, 本题使用的是前序遍历
  • 如何使用递归的方式判断两棵子树是否相等? 递归终止条件是什么? 什么情况下能继续递归? 什么情况下不再进行递归? 下面大致说一说
  • root2.val != root2.val, 那么就不用递归了, 直接返回false
  • root1.val == root2.val, 需要继续判断,root1.left.val是否等于root2.left.val; 同时还要判断root1.right.val是否等于root2.right.val. 相当于同时对root1的左右子节点进行判断
  • 上面的过程看起来处理: root1, root1.left, root1.right
  • 实际上只处理root1. 然后使用递归去处理root1.left和root1.right
  • 看淡左右子节点这个概念, 向递归函数中传入子节点, 其在递归函数中是按照新的根节点对待
  • 上面说了, 只有当前root1.val == root2.val时才会继续向下判断, 所以如果向下判断时, 传入的子节点是null, 意味着当前树枝判断完了. 如果root2 == null, 说明当前root2对应的叶子节点值和对应的root1的值相等, 也就是当前树枝是A中的一部分, 返回true. 需要B的所有叶子节点满足这个条件, 也就是只要有一个叶子节点不满足,就要返回false
  • 如果root2 != null; 但是 root1 == null, 说明A已经没有节点了,但是没有找到对应的B, 所以返回false;
  • 如果root2 != null, 同时 root1 != null, 判断当前的根节点是否满足条件, 满足的话接着调用递归函数判断当前根节点的左右子节点是否满足条件
  • 如果不满足的话, 直接返回false

第二次做, 递归版,稍微清晰些,开始判断两树是否相等的base case写错了

  • 这个方法太耗费时间空间,貌似高效的做法是把数序列化,然后使用KMP算法判断B是否为A的子串
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root2==null)
return false; //题目的约定,跟root1的遍历没有关系
//二叉树的遍历,A上的每个节点都可以作为根节点,然后去和B进行比较
if(root1==null)
return false;
boolean res = WhetherSub(root1, root2);
if(res)
return res;
res = HasSubtree(root1.left, root2);
if(res)
return res;
res = HasSubtree(root1.right, root2);
return res;
}
public boolean WhetherSub(TreeNode root1, TreeNode root2){
//base case
//这句要想清楚,root2==null,说明A,B之前的某一条路都相等
//检查完了root2的这条路,返回true
if(root2==null)
return true;
if(root1==null && root2 !=null)//root2!=null
return false;
//处理当前环境(条件):root1,rooot2
if(root1.val != root2.val)
return false;
//here, root1!=null && root2!=null && root1.val==root2.val
//使用新环境(条件):roo1.left, root2.left
boolean leftRes = WhetherSub(root1.left, root2.left);
//使用新环境(条件):root1.right, root2.right
boolean rightRes = WhetherSub(root1.right, root2.right);
return leftRes&&rightRes;
}
}
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
// input check
if(root1 == null || root2 == null)
return false;
// execute
boolean result=false;
if(root1.val == root2.val)
result = TellChildTree(root1, root2);
if(!result)
result = HasSubtree(root1.left, root2);
if(!result)
result = HasSubtree(root1.right, root2);
return result;
}
public boolean TellChildTree(TreeNode root1, TreeNode root2){
// 递归终止条件
if(root2 == null)
return true;
if(root1 == null)
return false;
// execute
if(root1.val == root2.val)
return TellChildTree(root1.left, root2.left) && TellChildTree(root1.right, root2.right);
return false;

}
}
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;

public TreeNode(int val) {
this.val = val;

}

}
*/