【Warrior刷题笔记】二叉树的之字形打印

# 题目一 剑指 Offer 32 - I. 从上到下打印二叉树
来源:力扣(LeetCode)

#### 1.描述
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

#### 2.示例
- 示例 1:
```
给定二叉树: [3,9,20,null,null,15,7],

3
/ \
9  20
/  \
15   7
返回:

[3,9,20,15,7]
```
### 解法一 广度优先遍历+辅助队列
#### 解题思路
看到题不难想到最简单的办法就是借助一个队列,对二叉树进行广度优先遍历。
1.如果根节点为空,直接返回空数组,否则入队根节点;
2.将队头节点值加入答案数组;
3.入队队头节点的非空子节点,将队头节点出队。
4.重复`2`,`3`过程直到队列为空。

####   代码
```cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
vector<int> ans;//存储答案
if(!root) return ans;//如果根节点为空,返回空答案数组
queue<TreeNode*> q;//辅助队列
q.push(root);//根节点入队
while(!q.empty()){//只要队列不为空
ans.push_back(q.front()->val);//将队头节点值加入答案数组
//入队队头节点的非空子节点
if(q.front()->left) q.push(q.front()->left);
if(q.front()->right) q.push(q.front()->right);
q.pop();//将队头节点出队
}
return ans;//返回答案
}
};
```
#### 复杂度分析
**时间复杂度:** `O(m)``m`二叉树节点数,遍历整棵二叉树需要`O(m)`时间。
**空间复杂度:** `O(m)`。存储答案和使用辅助队列的空间消耗。

### 解法二 深度优先搜索+辅助数组
#### 解题思路
除了解法一较为直观的广度优先搜索外,我们也可以使用深度优先搜索+辅助二维数组解决本题。
具体的,我们对二叉树进行深度优先搜索,同时维护一个层数`level`,初始时`level``0`。每进入一层递归,也即从当前节点遍历孩子节点时,层数就加一,然后通过层数作为下标访问二维数组将节点值加入对应层的一维数组,最后再将二维数组按层捋直即为最终答案。

#### 代码
```cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
vector<int> ans;//存储答案
if(!root) return ans;//如果根节点为空,返回空数组
vector<vector<int>> aid;//辅助数组
dfs(root, 0, aid);//深度优先遍历
for(auto a : aid) ans.insert(ans.end(), a.begin(), a.end());//将二维数组按层捋直
return ans; //返回答案
}

void dfs(TreeNode* root, int level, vector<vector<int>>& aid){
if(!root) return;//如果节点为空,返回
if(aid.size()<=level){//如果该层数组还未创建,创建之
vector<int> temp;
aid.push_back(temp);
}
aid[level].push_back(root->val);//将节点值加入对应层数组
dfs(root->left, level+1, aid);//遍历左子节点,同时level+1
dfs(root->right, level+1, aid);//遍历右子节点,同时level+1
}
};
```
#### 复杂度分析
**时间复杂度:** `O(m)`。递归以及把二维数组捋直的时间消耗
**空间复杂度:** `O(m)`。递归以及辅助数组、答案数组的空间消耗。

# 题目二 剑指 Offer 32 - II. 从上到下打印二叉树 II
来源:力扣(LeetCode)

#### 1.描述
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

#### 2.示例
- 示例 1:
```
给定二叉树: [3,9,20,null,null,15,7],

3
/ \
9  20
/  \
15   7
返回其层次遍历结果:

[
[3],
[9,20],
[15,7]
]
```
### 解法一 广度优先搜索+辅助队列
#### 解题思路
参考题目一,我们可以写出广度优先搜索+辅助队列的版本。
不过略有不同的是,我们需要选择合适的方式进行分层。这里使用两个额外变量`count``size`进行分层操作,对每一层,`size`为层大小,初始时为`1`,即只有根节点的时候队列的大小。每记录一个节点,`count`值加一,当`count`等于`size`时,表示当前层已遍历完毕,则更新层大小,重置`count``0`
1.如果根节点为空,直接返回空数组,否则入队根节点;
2.将队头节点值加入答案数组;
3.入队队头节点的非空子节点,将队头节点出队;
4.将`count`加一,若`count`等于`size`,则更新`size`,并重置`count``0`
5.重复`2`,`3``4`过程直到队列为空。

####   代码
```cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;//存储答案
if(!root) return ans;//如果根节点为空,返回空数组
queue<TreeNode*> q;//辅助队列
q.push(root);//入队根节点
int size = q.size(), count = 0;//初始化size和count
vector<int> temp;//辅助数组
while(!q.empty()){//只要队列不为空
temp.push_back(q.front()->val);//记录队头节点值
//入队队头节点的非空子节点
if(q.front()->left) q.push(q.front()->left);
if(q.front()->right) q.push(q.front()->right);
q.pop();//出队队头节点
++count;//将count+1
if(count == size){//若count==size,表示当前层已记录完毕
ans.push_back(temp);//将temp加入答案数组
temp.clear();//清空temp
count = 0;//重置count为0
size = q.size();//更新size
}
}
return ans;//返回答案
}
};
```
#### 复杂度分析
**时间复杂度:** `O(m)``m`为二叉树节点数,遍历整棵二叉树需要`O(m)`时间。
**空间复杂度:** `O(m)`。辅助队列,答案数组,辅助数组的空间消耗。

### 解法二 深度优先搜索+辅助数组
#### 解题思路
除了解法一较为直观的广度优先搜索外,我们也可以使用深度优先搜索+辅助二维数组解决本题。
具体的,我们对二叉树进行深度优先搜索,同时维护一个层数`level`,初始时`level``0`。每进入一层递归,也即从当前节点遍历孩子节点时,层数就加一,然后通过层数作为下标访问二维数组将节点值加入对应层的一维数组,遍历完成后即为最终答案。

#### 代码
```cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;//存储答案
if(!root) return ans;//如果根节点为空,返回空数组
dfs(root, 0, ans);//深度优先遍历
return ans;//返回答案
}

void dfs(TreeNode* root, int level, vector<vector<int>>& aid){//深度优先遍历
if(!root) return;//如果节点为空,直接返回
if(aid.size()<=level){//如果当前层数组还未创建,创建之
vector<int> temp;
aid.push_back(temp);
}
aid[level].push_back(root->val);//存入当前节点值到对应层
dfs(root->left, level+1, aid);//遍历左孩子,层数加一
dfs(root->right, level+1, aid);//遍历右孩子,层数加一
}
};
```
#### 复杂度分析
**时间复杂度:** `O(m)`。遍历二叉树的时间消耗。
**空间复杂度:** `O(m)`。存储答案和递归的栈空间消耗。

# 题目三 剑指 Offer 32 - III. 从上到下打印二叉树 III
来源:力扣(LeetCode)

#### 1.描述
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

#### 2.示例
- 示例 1:

```
给定二叉树: [3,9,20,null,null,15,7],

3
/ \
9  20
/  \
15   7
返回其层次遍历结果:

[
[3],
[20,9],
[15,7]
]
```
### 解法一 广度优先搜索+辅助队列
#### 解题思路
参考题目二,我们可以写出广度优先搜索+辅助队列的版本。
我们需要选择合适的方式进行分层。这里使用两个额外变量`count``size`进行分层操作,对每一层,`size`为层大小,初始时为`1`,即只有根节点的时候队列的大小。每记录一个节点,`count`值加一,当`count`等于`size`时,表示当前层已遍历完毕,则更新层大小,重置`count``0`。不过略有不同的是,我们需要在遍历完毕后对结果数组进行处理,也即翻转偶数层数组。处理完毕后即为答案。
1.如果根节点为空,直接返回空数组,否则入队根节点;
2.将队头节点值加入答案数组;
3.入队队头节点的非空子节点,将队头节点出队;
4.将`count`加一,若`count`等于`size`,则更新`size`,并重置`count``0`
5.重复`2`,`3``4`过程直到队列为空;
6.翻转偶数层结果数组。

####   代码
```cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;//存储答案
if(!root) return ans;//如果根节点为空,返回空数组
queue<TreeNode*> q;//辅助队列
q.push(root);//入队根节点
int size = q.size(), count = 0;//初始化size和count
vector<int> temp;//辅助数组
while(!q.empty()){//只要队列不为空
temp.push_back(q.front()->val);//记录队头节点值
//入队队头节点的非空子节点
if(q.front()->left) q.push(q.front()->left);
if(q.front()->right) q.push(q.front()->right);
q.pop();//出队队头节点
++count;//将count+1
if(count == size){//若count==size,表示当前层已记录完毕
ans.push_back(temp);//将temp加入答案数组
temp.clear();//清空temp
count = 0;//重置count为0
size = q.size();//更新size
}
}
for(int i = 0; i < m; ++i) if(i%2!=0) reverse(ans[i].begin(), ans[i].end());//翻转偶数层
return ans;//返回答案
}
};
```
#### 复杂度分析
**时间复杂度:** `O(m)``m`为二叉树节点数,遍历整棵二叉树和翻转偶数层需要`O(m)`时间。
**空间复杂度:** `O(m)`。辅助队列,答案数组,辅助数组的空间消耗。

### 解法二 深度优先遍历+辅助数组
#### 解题思路
这里也提供深度优先遍历的版本。

#### 代码
```cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;//存储答案
if(!root) return ans;//如果根节点为空,返回空数组
dfs(root, 0, ans);//深度优先遍历
for(int i = 0; i < m; ++i) if(i%2!=0) reverse(ans[i].begin(), ans[i].end());//翻转偶数层
return ans;//返回答案
}

void dfs(TreeNode* root, int level, vector<vector<int>>& aid){//深度优先遍历
if(!root) return;//如果节点为空,直接返回
if(aid.size()<=level){//如果当前层数组还未创建,创建之
vector<int> temp;
aid.push_back(temp);
}
aid[level].push_back(root->val);//存入当前节点值到对应层
dfs(root->left, level+1, aid);//遍历左孩子,层数加一
dfs(root->right, level+1, aid);//遍历右孩子,层数加一
}
};
```
#### 复杂度分析
**时间复杂度:** `O(m)`。遍历二叉树和翻转偶数层的时间消耗。
**空间复杂度:** `O(m)`。存储答案和递归的栈空间消耗。

一般说来,广度优先遍历更直观,但是代码较长;而深度优先遍历代码简洁,但是较难理解。要多用多练,以掌握之。



#打工人不该被pua##学习路径#
全部评论
链接已点,谢谢大佬
点赞 回复 分享
发布于 2022-01-16 17:31

相关推荐

11-09 11:01
济南大学 Java
Java抽象带篮子:外卖项目真得美化一下,可以看看我的详细的外卖话术帖子
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务