题目描述

✅ 993. 二叉树的堂兄弟节点

image-20260929105520768

image-20260929105520911

题意分析

判断值为 x、y 的两个节点是否同时满足“深度相同”和“父节点不同”。同一父节点的两个孩子是兄弟节点,即使同层也不能算堂兄弟。

题目保证节点值唯一,x、y 不同且都存在,可以直接按值识别目标。层序遍历负责确认深度,再记录各自父节点即可完成判断。

解法:层序遍历记录父节点

核心思路

[!blue]

队列在每轮开始时保存当前层节点。先固定 size = queue.size(),只弹出这 size 个节点;遍历中加入队列的孩子留到下一轮处理。这样就不会把当前层与下一层混在一起。

代码站在父节点处检查左右孩子。当前层所有节点深度相同,它们的孩子也都位于同一深度;发现目标孩子时,把当前节点记录到 xParent 或 yParent。这两个变量每轮重新置空,只代表同一个孩子层中的发现结果。

必须等整层扫描完再判断,因为另一个目标可能是本层后面某个节点的孩子。两个父节点都非空时,目标已经确认同层,只需比较父节点是否为同一个对象;只有一个非空时,该层只有一个目标,另一个目标深度必然不同,可以立即返回 false;都为空才继续搜索下一层。

每个非根节点只会作为自己唯一父节点的孩子被检查一次,因此这种方式不会遗漏目标,也不会把不同深度的发现拼在一起。根节点没有父节点,且深度零只有它一个节点,涉及根的两个不同节点不可能成为堂兄弟,当前搜索会自然返回 false。

解题步骤

  1. 将根入队,按层固定本轮节点数量。
  2. 检查本层各节点的左右孩子,记录目标对应的父节点。
  3. 层末找到两个则比较父节点,只找到一个则否定。
  4. 继续下一层或返回结果。

只把非空孩子加入队列,避免下一轮访问空节点。题目保证树非空,可以直接从根初始化队列;比较父节点使用节点身份,不需要额外保存深度或父节点数值。

代码实现

class Solution {
    public boolean isCousins(TreeNode root, int x, int y) {
        ArrayDeque<TreeNode> queue = new ArrayDeque<>();

        queue.offer(root);

        while (!queue.isEmpty()) {
            // 先固定本层节点数,避免把新入队的下一层卷进来。
            int size = queue.size();
            // 记录本轮发现的同一孩子层中,两个目标各自的父节点。
            TreeNode xParent = null;
            TreeNode yParent = null;

            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();

                if (node.left != null) {
                    // 站在父节点上发现孩子,才能同时拿到父指针。
                    if (node.left.val == x) {
                        xParent = node;
                    } else if (node.left.val == y) {
                        yParent = node;
                    }

                    queue.offer(node.left);
                }

                if (node.right != null) {
                    if (node.right.val == x) {
                        xParent = node;
                    } else if (node.right.val == y) {
                        yParent = node;
                    }

                    queue.offer(node.right);
                }
            }

            // 同层出现两者:只看父节点是否不同。
            if (xParent != null && yParent != null) {
                return xParent != yParent;
            }

            // 同层只出现一个:另一个深度必然不同。
            if (xParent != null || yParent != null) {
                return false;
            }
        }

        return false;
    }
}
func isCousins(root *TreeNode, x int, y int) bool {
    queue := []*TreeNode{
        root,
    }

    for len(queue) > 0 {
        // 先固定本层节点数,避免把新入队的下一层卷进来。
        size := len(queue)
        // 记录本轮发现的同一孩子层中,两个目标各自的父节点。
        var xParent, yParent *TreeNode

        for i := 0; i < size; i++ {
            node := queue[0]
            queue = queue[1:]

            if node.Left != nil {
                // 站在父节点上发现孩子,才能同时拿到父指针。
                if node.Left.Val == x {
                    xParent = node
                } else if node.Left.Val == y {
                    yParent = node
                }
                queue = append(queue, node.Left)
            }
            if node.Right != nil {
                if node.Right.Val == x {
                    xParent = node
                } else if node.Right.Val == y {
                    yParent = node
                }
                queue = append(queue, node.Right)
            }
        }

        // 同层出现两者:只看父节点是否不同。
        if xParent != nil && yParent != nil {
            return xParent != yParent
        }
        // 同层只出现一个:另一个深度必然不同。
        if xParent != nil || yParent != nil {
            return false
        }
    }
    return false
}

复杂度分析

  • 时间复杂度:$O(n)$,每个节点至多被扫描一次。
  • 空间复杂度:$O(w)$,w 为树的最大层宽,上界为 $O(n)$。

关键点总结

[!green]

  • 同层与不同父节点两个条件必须同时成立。
  • 从父节点检查孩子,可直接取得父节点身份。
  • 固定层大小,避免把下一层混入当前层。

易错点总结

[!yellow]

  • 只比较父节点不同:不同深度的节点也可能父节点不同。
  • 同层找到两者就直接返回 true:还需要排除亲兄弟。
  • 循环中使用不断变化的队列长度作为本层大小:层边界失效。
  • 孩子为空仍入队并读取:后续会访问空节点。

相似题目

题目 难度 关联与区别
102. 二叉树的层序遍历 中等 分层BFS可检查两个节点是否同层,再单独比较它们是否同父。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2022/87302493
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!