【Warrior刷题笔记】剑指offer 32. 三道题,让你学会二叉树的深度广度优先遍历与递归迭代技术
题目一 剑指 Offer 32 - I. 从上到下打印二叉树
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/
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过程直到队列为空。
代码
/**
* 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。每进入一层递归,也即从当前节点遍历孩子节点时,层数就加一,然后通过层数作为下标访问二维数组将节点值加入对应层的一维数组,最后再将二维数组按层捋直即为最终答案。
代码
/**
* 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)
链接:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/
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过程直到队列为空。
代码
/**
* 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。每进入一层递归,也即从当前节点遍历孩子节点时,层数就加一,然后通过层数作为下标访问二维数组将节点值加入对应层的一维数组,遍历完成后即为最终答案。
代码
/**
* 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)
链接:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/
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.翻转偶数层结果数组。
代码
/**
* 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)。辅助队列,答案数组,辅助数组的空间消耗。
解法二 深度优先遍历+辅助数组
解题思路
这里也提供深度优先遍历的版本。
代码
/**
* 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)。存储答案和递归的栈空间消耗。
一般说来,广度优先遍历更直观,但是代码较长;而深度优先遍历代码简洁,但是较难理解。要多用多练,以掌握之。
更多知识内容分享:
博客园个人主页https://home.cnblogs.com/u/newCoderTheWarrior
力扣个人主页https://leetcode-cn.com/profile/articles/

【Warrior刷题笔记】剑指offer 32. 三道题,让你学会二叉树的深度广度优先遍历与递归迭代技术的更多相关文章
- 【Java】 剑指offer(32) 从上往下打印二叉树
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 (一)从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺 ...
- 二维数组的查找,刷题成功——剑指Offer
今天又做了一道题目,通过啦,欧耶! https://www.nowcoder.net/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqI ...
- Leetcode刷题记录 剑指offer
面试题3:数组中重复数字 # 使用set,时间复杂度O(n),空间复杂度O(n)class Solution(object): def findRepeatNumber(self, nums): &q ...
- #刷题记录--剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字. 抓住一点,通过递归进行节点创建时,是按照 前序遍历数组 进行创建的. 根节点,根节点的左 ...
- 剑指offer——32从上到下打印二叉树
题目描述 从上往下打印出二叉树的每个节点,同层节点从左至右打印. 题解: 就是简单的层序遍历 class Solution { public: vector<int> PrintFro ...
- 剑指 Offer 32 - I. 从上到下打印二叉树 + 层次遍历二叉树
剑指 Offer 32 - I. 从上到下打印二叉树 Offer_32_1 题目描述 解题思路 这题属于简单题,考察的是我们对二叉树以及层次遍历的方法. 这里只需要使用简单的队列即可完成二叉树的层次遍 ...
- 剑指 Offer 32 - III. 从上到下打印二叉树 III
剑指 Offer 32 - III. 从上到下打印二叉树 III 请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印, ...
- 剑指 Offer 32 - I. 从上到下打印二叉树
剑指 Offer 32 - I. 从上到下打印二叉树 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印. 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 ...
- 剑指 Offer 32 - III. 从上到下打印二叉树 III + 双端队列使用 + 蛇形打印层次遍历序列 + 正倒序输出
剑指 Offer 32 - III. 从上到下打印二叉树 III Offer_32_3 题目详情 题解分析 本题我想的比较复杂,其实题目的要求只是需要遍历的结果逆序和正序交替,这个其实可以使用Coll ...
随机推荐
- 『与善仁』Appium基础 — 21、元素的基本操作
目录 1.元素的基本操作说明 (1)点击操作 (2)清空操作 (3)输入操作 2.综合练习 1.元素的基本操作说明 (1)点击操作 点击操作:click()方法.(同Selenium中使用方式一致) ...
- Nginx中指令
Rewrite模块 1 return指令 Syntax: return code [text]; return code URL; return URL; Default: - Context: se ...
- Memcached 状态机分析
worker线程拿到了这个连接之后,就应该是分配给这个连接一个结构体,包括这个连接所有的状态,都写buf等,这个结构体就是conn,然后这个worker线程会在它自己的event_base加入对这个新 ...
- Git remote 远程仓库链接管理
SVN 使用单个集中仓库作为开发人员的通信枢纽,通过在开发人员的工作副本和中央仓库之间传递变更集来进行协作. 这与 Git 的分布式协作模型不同,后者为每个开发人员提供了自己的仓库副本,并具有自己的本 ...
- 返回记录中的指定内容Record.Field…(Power Query 之 M 语言)
返回指定字段: = Record.Field( 记录, "字段名") = Record.FieldOrDefault( 记录, "字段名", 找不到时返回的值) ...
- Jaeger的客户端采样配置(Java版)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- LuoguB2106 矩阵转置 题解
Content 给定一个 \(n\times m\) 的矩阵 \(A\),求其转置 \(A^\text T\). 数据范围:\(1\leqslant n,m\leqslant 100\). Solut ...
- 当ligerui的grid出现固定列与非固定列不在同一水平线上时,改怎么处理
当ligerui的grid出现固定列与非固定列不在同一水平线上时,如下图所示: 此时可以增加fixedCellHeight:false属性进行解决.这个属性在IE上不起作用,那么该怎么处理,可以这样处 ...
- 线程 TLS
TLS为什么产生呢?是软件开发中的什么问题呢? TLS 产生背景进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量.在一个线程修改的内存内容,对所有线程都生效 ...
- Centos7查看防火墙对应的开放端口以及进行端口操作
1.查看开放端口列表 [root@host bin]# firewall-cmd --list-ports 22/tcp 80/tcp 8888/tcp 39000-40000/tcp 12888/t ...