二叉树中如何判断两个节点是不是堂兄弟节点?
简化版
堂兄弟节点要求两个节点深度相同,但父节点不同。可以 BFS 按层遍历,在同一层记录目标节点的父节点;如果两个目标都在这一层且父节点不同,就是堂兄弟。
详细版
判断堂兄弟要同时看 depth 和 parent。
常用 BFS:
- 队列里存
(node, parent)。 - 每次处理一整层。
- 在当前层查找 x 和 y 的父节点。
- 如果当前层只找到一个,说明深度不同,返回 false。
- 如果两个都找到,比较父节点是否不同。
DFS 也可以,记录每个目标的深度和父节点。BFS 的优势是层级语义更直观,找到后可以提前结束。
完整版教学
一、堂兄弟节点的定义是什么
两个节点是堂兄弟,需要满足两个条件:深度相同,父节点不同。只满足一个不够。
1
/ \
2 3
/ \
4 5
4 和 5 深度相同,父节点分别是 2 和 3,所以是堂兄弟。2 和 3 深度相同,但它们是兄弟,不是堂兄弟,因为父节点相同。
二、为什么 BFS 很自然
BFS 一层一层遍历,天然能判断两个节点是否出现在同一深度。每处理一层,只在这一层查找目标。
第 0 层:1
第 1 层:2,3
第 2 层:4,5
如果 x 在第 1 层,y 在第 2 层,那么处理第 1 层时只找到 x,就可以知道它们不是堂兄弟。
三、为什么必须记录父节点
同一层的两个节点可能是兄弟,也可能是堂兄弟。只看层数无法区分。
队列元素可以存:
(node, parent)
当遇到 x 或 y 时,把 parent 记录下来。当前层结束后,如果两个 parent 都存在,就比较是否为同一个对象。
四、按层处理为什么比逐个处理安全
如果 BFS 不按层分组,遇到第一个目标就急着判断,可能还没扫描完同层的另一个目标。正确做法是每轮固定当前队列长度,处理完这一整层再做判断。
for (let i = 0; i < levelSize; i++) {
// 只处理当前层节点
}
levelSize 是层级边界。它让“同一深度”在代码里有明确范围。
五、DFS 怎么做
DFS 可以记录 x 和 y 的 (depth,parent)。遍历整棵树后比较:
x.depth == y.depth && x.parent != y.parent
DFS 写法也很简单,但需要小心父节点传参。BFS 更贴合“同层”的定义,通常更容易讲清。
六、边界和复杂度
如果树为空、只找到一个目标、两个目标值不存在,都不是堂兄弟。时间复杂度 O(n),空间复杂度 BFS 最坏 O(w),w 是最大宽度;DFS 递归栈 O(h)。
| 条件 | 是否堂兄弟 | 原因 |
|---|---|---|
| 深度相同、父节点不同 | 是 | 满足定义 |
| 深度相同、父节点相同 | 否 | 这是兄弟节点 |
| 深度不同 | 否 | 不在同一层 |
| 只找到一个目标 | 否 | 另一个节点不存在或值不匹配 |
记忆钩子:堂兄弟不是“同层就行”,还要“不是同一个父亲”。
七、常见误区与追问
- 误区:同一层的节点一定是堂兄弟。 同一父节点的兄弟不是堂兄弟。
- 误区:找到一个目标就能立刻返回。 需要处理完整层,确认另一个目标是否也在同层。
- 误区:比较父节点值就够。 更稳的是比较父节点对象;值可能重复。
- 追问:DFS 能做吗? 能,记录两个目标的深度和父节点后比较。
- 追问:复杂度是多少? 时间 O(n),BFS 空间最坏 O(w),DFS 栈空间 O(h)。
八、加强记忆
判断堂兄弟只需要两个信息:深度和父节点。BFS 按层天然拿深度,队列里带 parent 就能判断父节点是否不同。记住“同层不同父”,就不会把兄弟节点误判成堂兄弟。