力扣解题-100. 相同的树

23 阅读6分钟

力扣解题-100. 相同的树

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

image.png

输入:p = [1,2,3], q = [1,2,3] 输出:true

示例 2:

image.png

输入:p = [1,2], q = [1,null,2] 输出:false

示例 3:

image.png

输入:p = [1,2,1], q = [1,1,2] 输出:false

提示:

两棵树上的节点数目都在范围 [0, 100] 内

-10⁴ <= Node.val <= 10⁴

Related Topics

树、深度优先搜索、广度优先搜索、二叉树


第一次解答

解题思路

核心方法:递归法(深度优先搜索 DFS),通过“自顶向下”的递归对比,逐节点校验两棵树的结构和值是否一致,基线条件覆盖“双空、一空一非空、值不同”三种不/同情况,逻辑简洁且时间/空间复杂度最优。

核心逻辑拆解

判断两棵树是否相同的核心是“逐节点校验+递归验证子树”,需同时满足结构相同节点值相同

  1. 基线条件1(双空节点):若p == null && q == null,说明当前位置的节点都不存在,结构一致,返回true
  2. 基线条件2(结构不同):若p == null || q == null(仅一个节点为空),说明结构不一致,返回false
  3. 基线条件3(值不同):若p.val != q.val,说明节点值不一致,返回false
  4. 递归验证子树
    • 递归校验左子树:isSameTree(p.left, q.left)
    • 递归校验右子树:isSameTree(p.right, q.right)
  5. 合并结果:只有左右子树都相同(leftResult && rightResult),当前节点所在的子树才相同,返回最终布尔值。
具体步骤(以示例2 p=[1,2]、q=[1,null,2]为例)
递归层级对比节点(p,q)校验结果说明
1(1,1)-值相同,递归校验子树
2(2,null)false一空一非空,结构不同
2(null,2)未执行左子树已返回false,短路
最终结果为false,与示例一致。
性能说明
  • 时间复杂度:O(n)(n为两棵树中节点数的较小值,每个节点仅被对比一次);
  • 空间复杂度:O(h)(h为两棵树中高度的较小值,递归调用栈的深度等于树的高度):
    • 最好情况(平衡二叉树):h = log₂n,空间复杂度O(logn);
    • 最坏情况(斜树):h = n,空间复杂度O(n);
  • 优势:
    1. 代码极简,基线条件覆盖所有核心判断场景,逻辑无冗余;
    2. 短路特性:只要某一层级校验失败,后续递归直接终止,执行效率高;
    3. 天然处理空树、单节点树等边界场景。
    public boolean isSameTree(TreeNode p, TreeNode q) {
        // 如果两个节点都为空,则它们相同
        if (p == null && q == null) {
            return true;
        }
        // 如果一个节点为空,另一个不为空,则它们不相同
        if (p == null || q == null) {
            return false;
        }
        // 如果两个节点的值不相同,则它们不相同
        if (p.val != q.val) {
            return false;
        }
        // 递归判断左右子树是否相同
        boolean leftResult=isSameTree(p.left, q.left);
        boolean rightResult=isSameTree(p.right, q.right);

        return leftResult&&rightResult;
    }

示例解答

解题思路

解法1:迭代法(广度优先搜索 BFS / 层序遍历)

核心方法:队列辅助层序对比,利用队列同时存储两棵树的对应节点,逐层逐节点校验结构和值是否一致,属于“自顶向下”的迭代实现,避免递归栈的调用,适合树高度较大的场景。

代码实现
import java.util.LinkedList;
import java.util.Queue;

public boolean isSameTree(TreeNode p, TreeNode q) {
    // 队列存储两棵树的对应节点(成对存储)
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(p);
    queue.offer(q);
    
    while (!queue.isEmpty()) {
        // 取出一对节点进行对比
        TreeNode nodeP = queue.poll();
        TreeNode nodeQ = queue.poll();
        
        // 双空节点,跳过后续校验
        if (nodeP == null && nodeQ == null) {
            continue;
        }
        // 结构不同或值不同,直接返回false
        if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) {
            return false;
        }
        
        // 成对加入左右子节点(保证顺序一致)
        queue.offer(nodeP.left);
        queue.offer(nodeQ.left);
        queue.offer(nodeP.right);
        queue.offer(nodeQ.right);
    }
    // 所有节点对比完成,均一致
    return true;
}
核心逻辑说明
  1. 队列初始化:将两棵树的根节点成对入队,保证对比顺序一致;
  2. 层序遍历对比
    • 每次从队列中取出一对节点(nodePnodeQ);
    • 双空节点:跳过(结构一致,无需处理);
    • 结构/值不一致:直接返回false
    • 结构和值一致:将左右子节点成对入队(保证后续对比顺序);
  3. 返回结果:队列遍历完成后,说明所有节点都对比一致,返回true
性能说明
  • 时间复杂度:O(n)(每个节点仅入队/出队一次);
  • 空间复杂度:O(n)(最坏情况队列存储一层所有节点对,如完全二叉树的最后一层);
  • 优势:非递归实现,避免递归栈溢出风险(如树高度极大时);
  • 劣势:需要额外的队列空间,代码量略多于递归法。
解法2:迭代法(深度优先搜索 DFS / 栈模拟)

核心方法:栈模拟递归过程,用栈存储两棵树的对应节点,按“根→右→左”的顺序入栈(模拟递归的深度优先遍历),逐节点校验结构和值是否一致,逻辑与递归法等价但无递归栈开销。

代码实现
import java.util.Stack;

public boolean isSameTree(TreeNode p, TreeNode q) {
    Stack<TreeNode> stack = new Stack<>();
    // 根节点入栈
    stack.push(p);
    stack.push(q);
    
    while (!stack.isEmpty()) {
        // 取出一对节点(栈后进先出,先取q再取p)
        TreeNode nodeQ = stack.pop();
        TreeNode nodeP = stack.pop();
        
        // 双空节点,跳过
        if (nodeP == null && nodeQ == null) {
            continue;
        }
        // 结构/值不一致
        if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) {
            return false;
        }
        
        // 右子节点先入栈(保证左子节点先处理)
        stack.push(nodeP.right);
        stack.push(nodeQ.right);
        // 左子节点后入栈
        stack.push(nodeP.left);
        stack.push(nodeQ.left);
    }
    return true;
}
核心逻辑说明
  1. 栈初始化:将两棵树的根节点成对入栈;
  2. 栈遍历对比
    • 弹出一对节点(注意栈后进先出,先弹q再弹p);
    • 校验逻辑与递归法一致;
    • 按“右→左”顺序入栈子节点(保证左子节点先被处理,与递归DFS顺序一致);
  3. 返回结果:栈遍历完成后返回true
性能说明
  • 时间复杂度:O(n)(每个节点仅入栈/出栈一次);
  • 空间复杂度:O(h)(h为树的高度,栈的深度等于树的高度);
  • 优势:非递归实现,可控性更高,避免递归栈溢出;
  • 劣势:代码量多于递归法,需要手动管理栈的入栈/出栈顺序。

总结

  1. 递归DFS法(第一次解答):O(n)时间+O(h)空间,代码极简、逻辑直观,是判断相同树的最优解法,工程中优先使用;
  2. 迭代BFS法(层序遍历):O(n)时间+O(n)空间,非递归实现,适合需要避免递归栈的场景;
  3. 迭代DFS法(栈模拟):O(n)时间+O(h)空间,与递归法空间复杂度一致,代码稍复杂;
  4. 关键技巧:
    • 核心思想:判断相同树需同时满足“结构相同+节点值相同”,逐节点校验是核心;
    • 基线条件:优先判断“双空→一空一非空→值不同”,覆盖所有核心不一致场景;
    • 方法选择:优先选递归DFS法(代码简洁),树高度极大时选迭代法(避免栈溢出)。