剑指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);
    }
}
相关推荐
伊玛目的门徒4 小时前
试用leetcode之典中典 二数之和问题
java·算法·leetcode
Jerry4 小时前
LeetCode 226. 翻转二叉树
算法
想做小南娘,发现自己是女生喵5 小时前
【无标题】
数据结构·算法
Kx_Triumphs7 小时前
HDU4348 To the moon(主席树区间修改模板)
算法·题解
旖-旎7 小时前
《LeetCode647 回文子串 || LeetCode 5 最长回文子串》
c++·算法·leetcode·动态规划·哈希算法
轻颂呀8 小时前
约瑟夫环问题
算法
科技大视界10 小时前
投资AI项目,传统尽调不够用了——李章虎律师拆解算法、数据、算力三大雷区
人工智能·算法·数据挖掘
郝学胜-神的一滴10 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法
Irissgwe10 小时前
算法滑动窗口
数据结构·算法