【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 ... 
随机推荐
- jenkins pipeline语法
			目录 一.声明式 二.脚本式 基本 判断 异常处理 Steps node withEnv 一.声明式 声明式Pipeline必须包含在名为pipeline的语句块中,典型的声明式Pipeline语法如 ... 
- pipeline是什么?
			目录 一.pipeline是什么? 二.jenkinsfile是什么 三.pipeline语法选择 四.脚本式和声明式 五.插件与pipeline 一.pipeline是什么? pipeline是部署 ... 
- 代码图形统计工具git_stats web
			目录 一.简介 二.安装ruby 三.配置git_stats 四.通过nginx把网页展示出来 一.简介 仓库代码统计工具之一,可以按git提交人.提交次数.修改文件数.代码行数.注释量在时间维度上进 ... 
- [BUUCTF]PWN——CmmC_Simplerop
			cmcc_simplerop 附件 步骤 例行检查,32位,开启了nx保护 本地试运行一下程序,查看一下大概的情况 32位ida载入,习惯性的检索程序里的字符串,看了个寂寞,从main函数开始看程序 ... 
- Lookup函数(Excel函数集团)
			此处文章均为本妖原创,供下载.学习.探讨! 文章下载源是Office365国内版1Driver,如有链接问题请联系我. 请勿用于商业!谢谢 下载地址:https://officecommunity-m ... 
- CentOS7.6 鲜为人知的/etc/resolv.conf 之 /etc/resolv.conf.save (保持/etc/resolv.conf不被修改:/etc/dhcp/dhclient-enter-hooks 无效之/etc/resolv.conf被清空的特殊案例)
			目的: 用户可以自定义/etc/resolv.conf内容,且不被系统修改. 常规方法1: /etc/sysconfig/network-scripts/ifcfg-eth0 网卡配置文件中增加PEE ... 
- signal 信号
			python学习笔记--信号模块signal 阅读目录(Content) 1 signal基本信号名 2 常用信号处理函数 2.1 设置发送SIGALRM信号的定时器 2.2 设置信号处理函数 3 常 ... 
- 在react项目中使用require引入图片不生效
			如果使用create-react-app和require导入图像,require返回一个ES模块而不是字符串.这是因为在file-loader中,esModule选项是默认启用的. 用以下方式之一导入 ... 
- table中tr、td标签设置只读,不能修改(readonly属性)
			在不能修改的位置加上代码:onselectstart="return false" οnselect="document.selection.empty()" ... 
- 一个c++11自定义的信号量
			1.关于 This is from here But I did some changes. 2. semaphore.h /** @ brief : this is from https://sta ... 
