同学们,我把《剑指Offer》中所有“树”标签的习题及思路整理出来了,对照学习效果更佳:
题目分析:
递归!递归!递归!遇到树的问题首先想到递归!
判断是不是子结构——就是要判断一棵树能否包含另一棵树——遍历两个树看是否一致
举个栗子:如图所示,给定两棵树:
- 首先判断根节点,也就是
3
和4
,如果根节点相等,就继续同时遍历两棵树,看其他部分是否相同,我们把上述遍历来看是否完全相同的方法定义为recur
- 如果根节点不同,则递归到主树的左右子节点,也就是
4
和5
,分别判断这两个值和目标根节点是否相等,如果不相等再继续递归 - 此时我们发现
4
和目标根节点相同,则继续recur
其左右子节点即可 recur
函数用来判断从一个点出发能否完全匹配isSub
函数用来递归判断是不是左右子树的子结构
通俗来讲,给定两个点,先判断这两个点能否匹配(recur)
,如果不能,再判断左右子树能够匹配,这三个结果有一个是true
就可以
难点分析:什么时候跳出recur()
方法?
- 如果B树走到头,为
null
时,就返回true
,此时A树有两种情况:空或不为空,无论那种情况,都说明完全匹配 - else,如果B不为空,此时还是两种情况:A空,则说明没有完全匹配;A不空,则需要判断两个节点的值
- 若都满足,则进入子节点的比较,及分别比较左子节点和右子节点,都
true
才返回true
这道题类比《对称的二叉树》:判断对称的要求是完全一样,因此需要A==null && B==null
,但是这里判断子结构不能这样,当B==null
时,说明判断完成,而如果不满足上一条直接A==null
,则说明判断失败
代码如下:
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A==null || B==null) return false;
return (recur(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B));
}
private boolean recur(TreeNode A,TreeNode B) {
if(B==null) {
return true;
}
if(A==null || (A.val != B.val)) {
return false;
}
return recur(A.left,B.left) && recur(A.right,B.right);
}
}
复杂度分析:
- 时间复杂度:O(MN),因为首先要遍历主树M个节点,再去配对N个节点,因此是M*N
- 空间复杂度:O(M),M为A的节点个数,因为总递归深度就是A的节点数目