文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法一
- 思路和算法
- 代码
- 复杂度分析
- 解法二
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:奇偶树
出处:1609. 奇偶树
难度
4 级
题目描述
要求
如果一个二叉树满足下述条件,则称为奇偶树:
- 二叉树根结点所在层下标为 0 \texttt{0} 0,根结点的子结点所在层下标为 1 \texttt{1} 1,根结点的孙结点所在层下标为 2 \texttt{2} 2,依此类推。
- 偶数下标层上的所有结点的值都是奇整数,从左到右按顺序严格递增。
- 奇数下标层上的所有结点的值都是偶整数,从左到右按顺序严格递减。
给定二叉树的根结点,如果二叉树为奇偶树,则返回 true \texttt{true} true,否则返回 false \texttt{false} false。
示例
示例 1:
输入:
root
=
[1,10,4,3,null,7,9,12,8,6,null,null,2]
\texttt{root = [1,10,4,3,null,7,9,12,8,6,null,null,2]}
root = [1,10,4,3,null,7,9,12,8,6,null,null,2]
输出:
true
\texttt{true}
true
解释:每一层的结点值分别是:
第
0
\texttt{0}
0 层:
[1]
\texttt{[1]}
[1]
第
1
\texttt{1}
1 层:
[10,4]
\texttt{[10,4]}
[10,4]
第
2
\texttt{2}
2 层:
[3,7,9]
\texttt{[3,7,9]}
[3,7,9]
第
3
\texttt{3}
3 层:
[12,8,6,2]
\texttt{[12,8,6,2]}
[12,8,6,2]
由于第
0
\texttt{0}
0 层和第
2
\texttt{2}
2 层上的结点值都是奇数且严格递增,而第
1
\texttt{1}
1 层和第
3
\texttt{3}
3 层上的结点值都是偶数且严格递减,因此这是一个奇偶树。
示例 2:
输入:
root
=
[5,4,2,3,3,7]
\texttt{root = [5,4,2,3,3,7]}
root = [5,4,2,3,3,7]
输出:
false
\texttt{false}
false
解释:每一层的结点值分别是:
第
0
\texttt{0}
0 层:
[5]
\texttt{[5]}
[5]
第
1
\texttt{1}
1 层:
[4,2]
\texttt{[4,2]}
[4,2]
第
2
\texttt{2}
2 层:
[3,3,7]
\texttt{[3,3,7]}
[3,3,7]
第
2
\texttt{2}
2 层上的结点值不满足严格递增的条件,所以这不是一个奇偶树。
示例 3:
输入:
root
=
[5,9,1,3,5,7]
\texttt{root = [5,9,1,3,5,7]}
root = [5,9,1,3,5,7]
输出:
false
\texttt{false}
false
解释:第
1
\texttt{1}
1 层上的结点值应为偶数。
数据范围
- 树中结点数目在范围 [1, 10 5 ] \texttt{[1, 10}^\texttt{5}\texttt{]} [1, 105] 内
- 1 ≤ Node.val ≤ 10 6 \texttt{1} \le \texttt{Node.val} \le \texttt{10}^\texttt{6} 1≤Node.val≤106
解法一
思路和算法
为了判断二叉树是否为奇偶树,需要判断以下条件。
-
对于任意结点,结点值与结点所在层数的奇偶性不同。
-
偶数层的结点从左到右严格单调递增,奇数层的结点从左到右严格单调递减。
由于判断奇偶树的依据是每一层结点都要符合奇偶树的条件,因此可以使用层序遍历实现。
从根结点开始依次遍历每一层的结点,同一层的结点的遍历顺序为从左到右。在层序遍历的过程中需要区分不同结点所在的层,确保每一轮访问的结点为同一层的全部结点。遍历每一层结点之前首先得到当前层的结点数,即可确保每一轮访问的结点为同一层的全部结点。
每次访问结点时,将待访问的结点出队列,判断是否符合奇偶树的条件。
-
如果结点值与结点所在层数的奇偶性相同,则二叉树不是奇偶树。
-
如果当前访问的结点不是当前层的最后一个结点,则此时的队首元素即为下一个结点(注意当前结点已经出队列),比较当前结点值与下一个结点值的大小。如果当前层是偶数层且当前结点值大于等于下一个结点值,或者当前层是奇数层且当前结点值小于等于下一个结点值,则二叉树不是奇偶树。
遍历结束之后,如果没有发现不符合奇偶树的条件的情况,则二叉树为奇偶树。
代码
class Solution {
public boolean isEvenOddTree(TreeNode root) {
int level = -1;
Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
level++;
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (node.val % 2 == level % 2) {
return false;
}
if (i < size - 1) {
int nextVal = queue.peek().val;
if ((level % 2 == 0 && node.val >= nextVal) || (level % 2 == 1 && node.val <= nextVal)) {
return false;
}
}
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
return true;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是队列空间,队列内元素个数不超过 n n n。
解法二
思路和算法
也可以使用深度优先搜索判断二叉树是否为奇偶数。具体做法是使用前序遍历,依次访问二叉树的根结点、左子树和右子树,由于前序遍历满足同一层结点被访问的顺序为从左到右,因此可以在前序遍历过程中判断二叉树是否符合奇偶树的条件。遍历过程中需要维护二叉树每一层的最右侧结点值。
对于每个结点,判断是否符合奇偶树的条件。
-
如果结点值与结点所在层数的奇偶性相同,则二叉树不是奇偶树。
-
如果当前层已经有访问过的结点,则得到上一个结点值,即当前层已经访问的最右侧结点值,比较上一个结点值与当前结点值的大小。如果当前层是偶数层且上一个结点值大于等于当前结点值,或者当前层是奇数层且上一个结点值小于等于当前结点值,则二叉树不是奇偶树。
在访问每个结点之后,将当前层的最右侧结点值设为当前结点值。
遍历结束之后,如果没有发现不符合奇偶树的条件的情况,则二叉树为奇偶树。
代码
class Solution {
List<Integer> rightmost = new ArrayList<Integer>();
public boolean isEvenOddTree(TreeNode root) {
return dfs(root, 0);
}
public boolean dfs(TreeNode node, int level) {
if (node.val % 2 == level % 2) {
return false;
}
if (level < rightmost.size()) {
int prev = rightmost.get(level);
if ((level % 2 == 0 && prev >= node.val) || (level % 2 == 1 && prev <= node.val)) {
return false;
}
rightmost.set(level, node.val);
} else {
rightmost.add(node.val);
}
return (node.left == null || dfs(node.left, level + 1)) && (node.right == null || dfs(node.right, level + 1));
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是递归调用的栈空间以及存储每一层的最右侧结点值的列表,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)。