题目
标题和出处
标题:二叉树的堂兄弟结点
难度
3 级
题目描述
要求
给你具有唯一值的二叉树的根结点 root \texttt{root} root,以及树中两个不同结点的值 x \texttt{x} x 和 y \texttt{y} y,如果值 x \texttt{x} x 和 y \texttt{y} y 对应的结点是堂兄弟结点,返回 true \texttt{true} true,否则返回 false \texttt{false} false。
如果二叉树的两个结点深度相同且父结点不同,则它们是堂兄弟结点。
在二叉树中,根结点位于深度 0 \texttt{0} 0 处,每个深度为 k \texttt{k} k 的结点的子结点位于深度 k + 1 \texttt{k + 1} k + 1 处。
示例
示例 1:
输入:
root
=
[1,2,3,4],
x
=
4,
y
=
3
\texttt{root = [1,2,3,4], x = 4, y = 3}
root = [1,2,3,4], x = 4, y = 3
输出:
false
\texttt{false}
false
示例 2:
输入:
root
=
[1,2,3,null,4,null,5],
x
=
5,
y
=
4
\texttt{root = [1,2,3,null,4,null,5], x = 5, y = 4}
root = [1,2,3,null,4,null,5], x = 5, y = 4
输出:
true
\texttt{true}
true
示例 3:
输入:
root
=
[1,2,3,null,4],
x
=
2,
y
=
3
\texttt{root = [1,2,3,null,4], x = 2, y = 3}
root = [1,2,3,null,4], x = 2, y = 3
输出:
false
\texttt{false}
false
数据范围
- 树中结点数目在范围 [2, 100] \texttt{[2, 100]} [2, 100] 内
- 1 ≤ Node.val ≤ 100 \texttt{1} \le \texttt{Node.val} \le \texttt{100} 1≤Node.val≤100
- 每个结点的值都唯一
- x ≠ y \texttt{x} \ne \texttt{y} x=y
- x \texttt{x} x 和 y \texttt{y} y 是树中存在的结点值
解法一
思路和算法
根据堂兄弟结点的定义,两个结点是堂兄弟结点等价于两个结点的深度相同且父结点不同。为了判断两个结点是否是堂兄弟结点,需要得到两个结点的深度和父结点。
由于二叉树中每个结点的值都唯一,并且给定的两个结点值都是二叉树中存在的结点值,因此可以根据给定的两个结点值唯一地确定两个结点。可以使用深度优先搜索遍历二叉树中的全部结点,遍历过程中维护每个结点的深度和父结点信息,当两个结点都找到时即可停止遍历。
遍历结束之后,即可判断两个结点是否是堂兄弟结点。如果两个结点深度相同且父结点不同,则是堂兄弟结点,否则不是堂兄弟结点。
代码
class Solution {
int x, y;
int xDepth, yDepth;
TreeNode xParent, yParent;
public boolean isCousins(TreeNode root, int x, int y) {
this.x = x;
this.y = y;
this.xDepth = -1;
this.yDepth = -1;
this.xParent = null;
this.yParent = null;
findNodes(root, 0, null);
return xDepth == yDepth && xParent != yParent;
}
public void findNodes(TreeNode node, int depth, TreeNode parent) {
if (xDepth >= 0 && yDepth >= 0) {
return;
}
if (node.val == x) {
xDepth = depth;
xParent = parent;
} else if (node.val == y) {
yDepth = depth;
yParent = parent;
}
if (node.left != null) {
findNodes(node.left, depth + 1, node);
}
if (node.right != null) {
findNodes(node.right, depth + 1, node);
}
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点最多被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是递归调用的栈空间,取决于二叉树的高度,最差情况下二叉树的高度是 O ( n ) O(n) O(n)。
解法二
思路和算法
也可以使用广度优先搜索遍历二叉树并寻找两个结点。
广度优先搜索需要使用队列存储待访问的结点,初始时将根结点入队列。遍历过程中,每次将一个结点出队列,然后将该结点的非空子结点入队列,直到队列为空时遍历结束。
由于判断堂兄弟结点需要维护每个结点的深度,因此在广度优先搜索的过程中需要维护深度的信息。为了维护深度的信息,需要确保每一轮访问的结点为同一层的全部结点,做法是每一轮访问结点之前需要首先得到队列内的元素个数,此时队列内的元素为同一层的全部结点,然后访问这些结点,并将这些结点的非空子结点入队列。上述做法可以确保每一轮访问的结点为同一层的全部结点。
由于判断堂兄弟结点需要维护每个结点的父结点,因此在遍历过程中需要对访问到的每个结点的子结点判断是否是待寻找的结点。如果访问到的结点的一个子结点是其中一个待寻找的结点,则当前结点即为其中一个待寻找的结点的父结点。
和深度优先搜索一样,广度优先搜索不需要遍历二叉树中的所有结点,当两个结点都找到时即可停止遍历。
遍历结束之后,即可判断两个结点是否是堂兄弟结点。如果两个结点深度相同且父结点不同,则是堂兄弟结点,否则不是堂兄弟结点。
代码
class Solution {
public boolean isCousins(TreeNode root, int x, int y) {
int xDepth = -1, yDepth = -1;
TreeNode xParent = null, yParent = null;
int depth = 0;
Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
queue.offer(root);
while (!queue.isEmpty() && (xDepth < 0 || yDepth < 0)) {
depth++;
int size = queue.size();
for (int i = 0; i < size && (xDepth < 0 || yDepth < 0); i++) {
TreeNode node = queue.poll();
TreeNode left = node.left, right = node.right;
if (left != null) {
if (left.val == x) {
xDepth = depth;
xParent = node;
} else if (left.val == y) {
yDepth = depth;
yParent = node;
}
queue.offer(left);
}
if (right != null) {
if (right.val == x) {
xDepth = depth;
xParent = node;
} else if (right.val == y) {
yDepth = depth;
yParent = node;
}
queue.offer(right);
}
}
}
return xDepth == yDepth && xParent != yParent;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点最多被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是队列空间,队列内元素个数不超过 n n n。