LeetCode-637. 二叉树的层平均值
问题地址
LeetCode637. 二叉树的层平均值
问题描述
规则
- 给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
示例
- 示例一:
输入:
3
/ \
9 20
/ \
15 7
输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 , 第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11] 。
提示
- 节点值的范围在32位有符号整数范围内。
解析
解题思路
- 计算每一层的平均值,需要在搜索中存储每一层数据的和、每一层元素的个数;
- 遍历结束后每一层的平均值=每一层数据的和/每一层元素的个数;
数据操作分析
- 计算每一层的平均值,需要在搜索中存储每一层数据的和、每一层元素的个数;
- 遍历结束后每一层的平均值=每一层数据的和/每一层元素的个数;
复杂度分析
- 时间复杂度O(n),其中 n 是二叉树中的节点个数。
-
空间复杂度O(n),其中 n 是二叉树中的节点个数。
编码实现
public class LeetCode0637_AverageOfLevelsInBinaryTtree {
public List<Double> averageOfLevels(TreeNode root) {
//放每一层的数字
Map<Integer, List<Integer>> map = new HashMap<>();
dfs(root, 1, map);
int layerNum = map.keySet().size();
List<Double> res = new ArrayList<>(layerNum*2);
for (int i = 0; i < layerNum; i++) {
List<Integer> list = map.get(i + 1);
double sum = 0.0;
for (Integer num : list
) {
sum += num;
}
res.add(sum / list.size());
}
return res;
}
private void dfs(TreeNode root, int curLayerNum, Map<Integer, List<Integer>> map) {
if (root == null) return;
cale(curLayerNum, map, root);
if (root.left == null && root.right == null) {
return;
}
curLayerNum++;
if (root.left != null) dfs(root.left, curLayerNum, map);
if (root.right != null) dfs(root.right, curLayerNum, map);
}
private void cale(int curLayerNum, Map<Integer, List<Integer>> map, TreeNode root) {
List<Integer> tmp;
if (map.get(curLayerNum) == null) {
tmp = new ArrayList<>();
} else {
tmp = map.get(curLayerNum);
}
tmp.add(root.val);
map.put(curLayerNum, tmp);
}
官方解法
方法一:深度优先搜索
思路:
- 使用深度优先搜索计算二叉树的层平均值,需要维护两个数组,\textit{counts} 用于存储二叉树的每一层的节点数,\textit{sums} 用于存储二叉树的每一层的节点值之和。搜索过程中需要记录当前节点所在层,如果访问到的节点在第 i 层,则将 \textit{counts}[i] 的值加 1,并将该节点的值加到 \textit{sums}[i]。
- 遍历结束之后,第 i 层的平均值即为 \textit{sums}[i] / \textit{counts}[i]。
复杂度分析
- 时间复杂度 O(n),其中 n 是二叉树中的节点个数。
- 深度优先搜索需要对每个节点访问一次,对于每个节点,维护两个数组的时间复杂度都是 O(1),因此深度优先搜索的时间复杂度是 O(n)。
- 遍历结束之后计算每层的平均值的时间复杂度是 O(h),其中 h 是二叉树的高度,任何情况下都满足 h \le n。
- 因此总时间复杂度是 O(n)。
- 空间复杂度:O(n),其中 n 是二叉树中的节点个数。空间复杂度取决于两个数组的大小和递归调用的层数,两个数组的大小都等于二叉树的高度,递归调用的层数不会超过二叉树的高度,最坏情况下,二叉树的高度等于节点个数。
编码实现
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Integer> counts = new ArrayList<Integer>();
List<Double> sums = new ArrayList<Double>();
dfs(root, 0, counts, sums);
List<Double> averages = new ArrayList<Double>();
int size = sums.size();
for (int i = 0; i < size; i++) {
averages.add(sums.get(i) / counts.get(i));
}
return averages;
}
public void dfs(TreeNode root, int level, List<Integer> counts, List<Double> sums) {
if (root == null) {
return;
}
if (level < sums.size()) {
sums.set(level, sums.get(level) + root.val);
counts.set(level, counts.get(level) + 1);
} else {
sums.add(1.0 * root.val);
counts.add(1);
}
dfs(root.left, level + 1, counts, sums);
dfs(root.right, level + 1, counts, sums);
}
}
方法二:广度优先搜索
思路:
- 也可以使用广度优先搜索计算二叉树的层平均值。从根节点开始搜索,每一轮遍历同一层的全部节点,计算该层的节点数以及该层的节点值之和,然后计算该层的平均值。
- 如何确保每一轮遍历的是同一层的全部节点呢?我们可以借鉴层次遍历的做法,广度优先搜索使用队列存储待访问节点,只要确保在每一轮遍历时,队列中的节点是同一层的全部节点即可。具体做法如下:
- 初始时,将根节点加入队列;
- 每一轮遍历时,将队列中的节点全部取出,计算这些节点的数量以及它们的节点值之和,并计算这些节点的平均值,然后将这些节点的全部非空子节点加入队列,重复上述操作直到队列为空,遍历结束。
- 由于初始时队列中只有根节点,满足队列中的节点是同一层的全部节点,每一轮遍历时都会将队列中的当前层节点全部取出,并将下一层的全部节点加入队列,因此可以确保每一轮遍历的是同一层的全部节点。
- 具体实现方面,可以在每一轮遍历之前获得队列中的节点数量 \textit{size}size,遍历时只遍历 \textit{size}size 个节点,即可满足每一轮遍历的是同一层的全部节点。
复杂度分析
- 时间复杂度 O(n),其中 n 是二叉树中的节点个数。
- 广度优先搜索需要对每个节点访问一次,时间复杂度是 O(n)。
- 需要对二叉树的每一层计算平均值,时间复杂度是 O(h),其中 h 是二叉树的高度,任何情况下都满足 h \le n。
- 因此总时间复杂度是 O(n)。
- 空间复杂度:O(n),其中 n 是二叉树中的节点个数。空间复杂度取决于队列开销,队列中的节点个数不会超过 n。
编码实现
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> averages = new ArrayList<Double>();
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
double sum = 0;
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
sum += node.val;
TreeNode left = node.left, right = node.right;
if (left != null) {
queue.offer(left);
}
if (right != null) {
queue.offer(right);
}
}
averages.add(sum / size);
}
return averages;
}
}
精彩评论
跳转地址1:637. 二叉树的层平均值:【二叉树层序遍历】详解!
思路
- 这道题目就是考察二叉树的层序遍历。
- 二叉树的层序遍历还有这两道题目,大家可以先做一下,其实都是一个思路
- 102.二叉树的层序遍历 单纯的层寻遍历
- 107.二叉树的层次遍历II 相对于102.二叉树的层序遍历就是把每一层倒叙输出就可以了
- 想要学习二叉树的深度遍历可以看这里彻底吃透前中后序递归法。
- 而本题呢,就是把每一层的结果求一个和,然后取平均值。
- 层序遍历一个二叉树,需要借用一个辅助数据结构队列来实现,队列先进先出,符合一层一层遍历的逻辑,而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
- 使用队列实现广度优先遍历,动画如下:
这样就实现了层序从左到右遍历二叉树。
编码实现
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<double> result;
while (!que.empty()) {
int size = que.size();
double sum = 0; // 统计每一层的和
for (int i = 0; i < size; i++) { // 这里一定要使用固定大小size,不要使用que.size()
TreeNode* node = que.front();
que.pop();
sum += node->val;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(sum / size); // 将每一层放进结果集
}
return result;
}
};