剑指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);
    }
}
相关推荐
咖啡八杯1 小时前
GoF设计模式——装饰模式
java·算法·设计模式·装饰器模式
装不满的克莱因瓶1 小时前
实现矩阵的点积:从数学原理到 NumPy 实战
人工智能·线性代数·算法·机器学习·矩阵·numpy
HZ·湘怡1 小时前
树 的定义 与 性质
算法·
梦想的颜色1 小时前
Docker 入门指南:从零开始掌握容器化技术
运维·服务器·vscode·python·算法·docker·云原生
cpp_25011 小时前
P10109 [GESP202312 六级] 工作沟通
数据结构·c++·算法·题解·洛谷·gesp六级
吴可可1231 小时前
CAD二次开发中多段线定点分割技巧
算法
ʚ希希ɞ ྀ1 小时前
全排列 --- 回溯
算法·leetcode·深度优先
玉树临风ives1 小时前
atcoder ABC 460 题解
数据结构·c++·算法
水无痕simon1 小时前
9 C语言的基础练习
c语言·开发语言·算法