剑指Offer LCR 143.子结构判断

一、思路:如果树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);
    }
}
相关推荐
To_OC2 小时前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
鱼鱼不愚与6 小时前
《原来如此 | 第01期:为什么导航软件能预测红绿灯倒计时?》
算法
复杂网络11 小时前
论最小 Agent 计算机的形态
算法
kisshyshy1 天前
🍦 雪糕、食堂、火车厢:三幅漫画吃透栈、队列与链表
javascript·算法
猿人谷1 天前
不只是 CPU 阈值:STAR 如何用 GAT + Transformer 做容器级自动扩缩容?
人工智能·算法
复杂网络1 天前
Stable Diffusion 视觉大模型微调技术深度调研
算法
复杂网络1 天前
基于 Stable Diffusion 架构的视觉大模型代表性工作与原理深度解析
算法
MrZhao4001 天前
Agent Loop 如何用 Hook 扩展:权限、日志与工具拦截
算法
MrZhao4001 天前
Agent 为什么需要 Skills:别把所有知识都塞进 system prompt
算法
JieE2123 天前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法