【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

相关推荐

10-23 16:33
门头沟学院 Java
本人某中9本科,成绩中等,目前没科研没实习,目前后端学到了javaWeb,开始没定好方向,在学国外课程,走工程路线起步有点晚了,到这个时间点了还在学JavaWeb,顿感迷茫,不知道是坚持走下去还是寒假去准备考研。考研这个路弄得我还是心痒痒的,因为从众考研的人也不在少数,所以会有这方面的心理安慰吧,就是“不行我可以去考研啊”,而且意味着三年的缓冲,为了复试还有积攒经验美化简历,其实现在也可以去申入实验室打杂;就业可能意味着多些工作经验,工程岗应该到后面还是经验大于学历?还是有点迷茫了,求助好心人有无路线启发
千千倩倩:同27给点建议,现在这个时间点可以快速看完外卖和点评,不用跟着敲,但一定要在看的时候总结每个部分的整个业务流程,对其中的实现有一个大概的印象。然后直接开始看八股,刷算法。八股和算法最好还是在项目学习中穿插着看。如果计算机基础,算法这些基础好,加上每天刻苦学习,两周可以达到勉强能面试的水平,到时候就直接海投中小厂,在约面和面试的过程中不断巩固知识。没找到实习也没关系,就当积累经验。再沉淀一波直接明年三月开始投暑期,毕竟是9本,总是有面试机会的,只要你这三个月不懈怠,面试发挥得一定不错,只要拿到一个中,大厂暑期实习,秋招就有竞争力了。总得而言,现在还有机会,但是时间非常紧张,需要你结合自己情况考虑,共勉
你会选择考研还是直接就业
点赞 评论 收藏
分享
自来熟的放鸽子能手面...:这个不一定,找hr跟进一下
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务