
一、思路:如果树B是树A的子结构,则子结构的根节点可能为树A的任意一个节点。因此,判断树B是否是树A的子结构,需要完成以下两步工作:
1.先序遍历树A中的每个节点node(对应函数isSubStructure(A,B))。
2.判断树A中以node为根节点的子树是否包含树B(对应函数dfs)。

二、算法流程:
1.规定树A的根节点记作节点A,树B的根节点记作节点B。
2.对于isSubStructure(A,B)函数:
(a)特例处理:当树A为空或者树B为空时,直接返回false(根据题意,空树不是任何树的子结构)。
(b)返回值:如果树B是树A的子结构,则必须满足以下三种情况之一,因此用或||连接。
------以节点A为根节点的子树包含树B,对应dfs(A,B)函数成立;
------树B是树A左子树的子结构,对应isSubStructure(A.left,B);
------树B是树A右子树的子结构,对应isSubStructure(A.right,B);
3.对于dfs(A,B)函数:
(1)终止条件:
(a)当节点B为空:说明树B已经匹配完成(越过叶子节点),因此返回true。
(b)当节点A为空:说明已经越过树A的叶子节点,即匹配失败,因此返回false。
(c)当节点A和B的值不同:说明匹配失败,返回false。
(2)返回值:
(a)判断A和B的左子节点是否相等,即dfs(A.left,B.left);
(b)判断A和B的右子节点是否相等,即dfs(A.right,B.right);
三、复杂度分析:
1.时间复杂度:O(MN),其中M和N分别为树A和树B的节点数量。先序遍历树A占用O(M),每次调用dfs(A,B)判断占用O(N)。
2.空间复杂度:O(M),当树A和树B都退化为链表时,递归调用的深度最大。当M <= N时,遍历树A与递归判断的总递归深度为M;当M > N时,最差情况为遍历至树A的叶子节点,此时的总递归深度为M。
附代码:
java
class Solution {
// 判断B是否是A的子结构
public boolean isSubStructure(TreeNode A, TreeNode B) {
// 根据题意:空树不是任何树的子结构
return (A != null && B != null) &&
(dfs(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
}
// 深度优先遍历,从A和B的当前节点开始同步向下比对
private boolean dfs(TreeNode A, TreeNode B) {
// B已经遍历完,说明匹配成功
if (B == null) {
return true;
}
// A已经遍历完 或 节点值不相等,匹配失败
if (A == null || A.val != B.val) {
return false;
}
// 继续比较左右子树
return dfs(A.left, B.left) && dfs(A.right, B.right);
}
}
ACM模式:
java
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
import java.util.Queue;
// 定义二叉树节点类
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取第一行:树A的层序遍历序列
String inputA = scanner.nextLine().trim();
// 读取第二行:树B的层序遍历序列
String inputB = scanner.nextLine().trim();
// 构建二叉树A和B
TreeNode rootA = buildTree(inputA);
TreeNode rootB = buildTree(inputB);
// 判断B是否是A的子结构
Solution solution = new Solution();
boolean result = solution.isSubStructure(rootA, rootB);
// 输出结果
System.out.println(result);
scanner.close();
}
// 根据输入字符串构建二叉树(层序遍历格式,null表示空节点)
private static TreeNode buildTree(String input) {
if (input == null || input.length() == 0 || input.equals("null")) {
return null;
}
String[] values = input.split(" ");
if (values.length == 0 || values[0].equals("null")) {
return null;
}
TreeNode root = new TreeNode(Integer.parseInt(values[0]));
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
int i = 1;
while (!queue.isEmpty() && i < values.length) {
TreeNode current = queue.remove();
// 处理左子节点
if (i < values.length && !values[i].equals("null")) {
current.left = new TreeNode(Integer.parseInt(values[i]));
queue.add(current.left);
}
i++;
// 处理右子节点
if (i < values.length && !values[i].equals("null")) {
current.right = new TreeNode(Integer.parseInt(values[i]));
queue.add(current.right);
}
i++;
}
return root;
}
}
// 解题类,包含判断子结构的方法
class Solution {
// 判断B是否是A的子结构
public boolean isSubStructure(TreeNode A, TreeNode B) {
// 根据题意:空树不是任何树的子结构
return (A != null && B != null) &&
(dfs(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B));
}
// 深度优先遍历,从A和B的当前节点开始同步向下比对
private boolean dfs(TreeNode A, TreeNode B) {
// B已经遍历完,说明匹配成功
if (B == null) {
return true;
}
// A已经遍历完 或 节点值不相等,匹配失败
if (A == null || A.val != B.val) {
return false;
}
// 继续比较左右子树
return dfs(A.left, B.left) && dfs(A.right, B.right);
}
}