题目
给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
示例 1:
给定的树 s:
3
/\
4 5
/\
1 2
给定的树 t:
4
/\
1 2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。
示例 2:
给定的树 s:
3
/\
4 5
/\
1 2
/
0
给定的树 t:
4
/\
1 2
返回 false。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subtree-of-another-tree
解题
暴力解法
运用DFS遍历,遍历树s的每个结点,将其当作根节点再进行遍历输出遍历结果(即该结点作为根节点的子树),与树t比较。
简言之就是枚举树s的所有子树,与树t相比较,看是否有相等的。
用字符串来记录树的遍历结果。
注意:
① 字符串记录的时候结点与结点间要用符号区别开,这里用的是“,”。不然比如一个结点值为12的,和两个结点分别值为1和2的在字符串中串联无法区分开来。
② 需要将二叉树填充为满二叉树,也就是对于满二叉树来说空的结点遍历时也需要记录,否则忽略的话只记录值会造成树的结构不同但遍历出的值是相同的情况。
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
boolean res=false;
String strt=dfs(t,"");
Stack<TreeNode> stack=new Stack<>();
stack.push(s);
while(!stack.isEmpty()){//栈dfs依次遍历树s的每个结点,并将每个结点当作根节点与树t的根节点比较,是否相等,判断s的一个子树是否与树t相等。
TreeNode node=stack.pop();
String strs=dfs(node,"");
if(node.val==t.val&&strs.equals(strt)&&strs!=""&&strt!=""){
return res=true;
}else{
if(node.left!=null) stack.push(node.left);
if(node.right!=null) stack.push(node.right);
}
}
return res;
}
//树的dfs遍历,输出遍历结果为一个字符串,将空结点也要输出结点
public String dfs(TreeNode root,String str){
str=str.concat(","+String.valueOf(root.val));
if(root.left!=null){
str=dfs(root.left,str);
}else{
str=str+","+"l";//叶子结点对应的左空结点记为"l",填补为完全二叉树,用来判断两个二叉树的结构是否相等。因为结构不一样的遍历结果的顺序也可能一样
}
if(root.right!=null){
str = dfs(root.right,str);
}else{
str = str+","+"r";叶子结点对应的右空结点记为"r"
}
return str;
}
}
优化(相当于串匹配):
在对比s的子树是否与t有相等的时候,不用dfs遍历树s枚举出s的所有子树一一比较,只需判断s的dfs遍历结果是否包含t的遍历结果即可。注意点同上。少了一层遍历优化了时间。
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
boolean res=false;
String strt=dfs(t,"");
String strs=dfs(s,"");
//判断t的遍历字符序列是否是s的遍历字符序列的子集
if(strs.indexOf(strt)!=-1){//不等于-1说明strt在strs中可以检索到
return res = true;
}
return res;
}
//树的dfs遍历,输出遍历结果为一个字符串,将空结点也要输出结点
public String dfs(TreeNode root,String str){
str=str+","+String.valueOf(root.val);
if(root.left!=null){
str=dfs(root.left,str);
}else{
str=str+","+ "l";//叶子结点对应的左结点为空记为"l",填补为完全二叉树,用来判断两个二叉树的结构是否相等。因为结构不一样的遍历结果的顺序也可能一样
}
if(root.right!=null){
str = dfs(root.right,str);
}else{
str = str+","+"r";叶子结点对应的右结点为空记为"r"
}
return str;
}
}
递归法
递归法的关键就在于,确定边界条件。
判断t是否为s的子树有三种情况:
- t 与 s 相等;
- 或,t 和 s 的左子树相等;
- 或,t 和 s 的右子树相等。
此时延伸到下一个问题:
判断s和t两个树是否相等有三个条件:
- 当前两个树的根节点值相等;
- and,s 的左子树和 t 的左子树相等;
- and,s 的右子树和 t 的右子树相等。
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if (t == null) return true; // t 为 null 一定都是 true
if (s == null) return false; // 这里 t 一定不为 null, 只要 s 为 null,肯定是 false
return isSubtree(s.left, t) || isSubtree(s.right, t) || isSameTree(s,t);
}
/**
* 判断两棵树是否相同
*/
public boolean isSameTree(TreeNode s, TreeNode t){
if (s == null && t == null) return true;
if (s == null || t == null) return false;
if (s.val != t.val) return false;
return isSameTree(s.left, t.left) && isSameTree(s.right, t.right);
}
}