二叉树的前序、中序、后序、层序遍历,递归和迭代两大类解题思路,每类细分不同解法【完整版】附PDF文档

本文主要是介绍二叉树的前序、中序、后序、层序遍历,递归和迭代两大类解题思路,每类细分不同解法【完整版】附PDF文档,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

二叉树文章系列:

  1. 二叉树的前序遍历
  2. 二叉树的中序遍历
  3. 二叉树的后序遍历
  4. 二叉树的层序遍历
  5. 二叉树的前序、中序、后序、层序遍历【解法完整版】

本文目录

    • 一、二叉树的前序遍历
      • 1.1 解题思路:递归
      • 1.2 解题思路:迭代(方法1)
      • 1.3 解题思路:迭代(方法2)
    • 二、二叉树的中序遍历
      • 2.1 解题思路:递归
      • 2.2 解题思路:迭代
    • 三、二叉树的后序遍历
      • 3.1 解题思路:递归
      • 3.2 解题思路:迭代(方法1)
      • 3.3 解题思路:迭代(方法2)
      • 3.4 解题思路:迭代(方法3)
    • 四、二叉树的层序遍历
      • 4.1 解题思路:广度优先搜索BFS
      • 4.2 解题思路:深度优先搜索DFS

一、二叉树的前序遍历

二叉树的前序遍历的记忆法则是“根左右",即先遍历根节点,再遍历左子树节点,再遍历右子树节点。

以上图为例,前序遍历的结果是【A, B, D, E, C, F, G】

1.1 解题思路:递归

递归是我们实现前中后序遍历最常用的方法。

什么问题可以采用递归求解呢?

需要满足三个条件:

  1. 一个问题的解可以分解为若干个子问题的解;
  2. 这个问题与分解的子问题,除了数据规模不同外,求解思路相同
  3. 存在递归终止条件。

那么在知道一个问题可以采用递归实现之后,如何写出递归代码呢?

关键在于能写出递归公式,找到终止条件。

在二叉树的前序遍历问题上,它的递归公式是:

preorder(node) = print node —> preorder(node->left) --> preorder(node->right)

它的终止条件是:

node 是否为空,为空则返回。

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;preorder(res, root);return res;}void preorder(vector<int>& res, TreeNode* root){if(!root) return;res.emplace_back(root->val);preorder(res, root->left);preorder(res, root->right);}
};

1.2 解题思路:迭代(方法1)

在递归方法实现过程中,它的底层是基于系统栈的结构来实现的。因此,我们可以使用栈的数据结构来辅助实现基于迭代方式的前序遍历。

具体思路为:

  • 初始化栈stack,初始化输出列表res
  • 根节点入栈
  • while(栈不为空),在循环体内部:
    • 栈顶元素出栈
    • 栈顶元素添加到输出列表
    • 如果栈顶元素的右子树节点不为空,将右子树节点入栈
    • 如果栈顶元素的左子树节点不为空,将左子树节点入栈
  • 返回输出列表res

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;if(!root) return res;stack<TreeNode*> s;s.push(root);while(!s.empty()){TreeNode* node = s.top();s.pop();res.emplace_back(node->val);if(node->right) s.push(node->right);if(node->left) s.push(node->left);}return res;}
};

1.3 解题思路:迭代(方法2)

基于迭代方法的第二种思路如下:

  • 初始化栈stack,初始化输出列表res
  • 设置一个变量cur, 表示当前节点。并赋初始值为根节点root
  • while(栈不为空 或者 当前节点cur不为空),在循环体内部:
    • 沿着当前节点的左分支一直走,直到为空。在这个过程中将遍历的节点都入栈,同时添加到输出列表
    • 栈顶元素出栈
    • 更新当前节点cur为栈顶元素的右子树节点。
  • 返回输出列表res

以图中的二叉树为例,来一步步来展示这个过程:

初始时,当前节点cur = root ,即节点A







class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;if(!root) return res;TreeNode* cur = root;stack<TreeNode*> s;while(!s.empty() || cur){// 沿着当前节点cur的左分支一直走到底while(cur){s.push(cur);res.emplace_back(cur->val);cur = cur->left;}TreeNode* node = s.top();s.pop();cur = node->right;}return res;}
};

二、二叉树的中序遍历

二叉树的中序遍历的记忆法则是“左根右",即先遍历左子树节点,再遍历根节点,再遍历右子树节点。

以上图为例,中序遍历的结果是【D, B, E, A, F, C, G】

2.1 解题思路:递归

递归是我们实现前中后序遍历最常用的方法。

什么问题可以采用递归求解呢?

需要满足三个条件:

  1. 一个问题的解可以分解为若干个子问题的解;
  2. 这个问题与分解的子问题,除了数据规模不同外,求解思路相同
  3. 存在递归终止条件。

那么在知道一个问题可以采用递归实现之后,如何写出递归代码呢?

关键在于能写出递归公式,找到终止条件。

在二叉树的中序遍历问题上,它的递归公式是:

preorder(node) = preorder(node->left) --> print node —> preorder(node->right)

它的终止条件是:

node 是否为空,为空则返回。

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;inorder(res, root);return res;}void inorder(vector<int>& res , TreeNode* root){if(!root) return; inorder(res, root->left);res.emplace_back(root->val);inorder(res, root->right);}
};

2.2 解题思路:迭代

基于迭代方法的思路如下:

  • 初始化栈stack,初始化输出列表res
  • 设置一个变量cur, 表示当前节点。并赋初始值为根节点root
  • while(栈不为空 或者 当前节点cur不为空),在循环体内部:
    • 沿着当前节点的左分支一直走,直到为空。在这个过程中将遍历的节点都入栈
    • 栈顶元素出栈,同时将栈顶元素添加到输出列表
    • 更新当前节点cur为栈顶元素的右子树节点。
  • 返回输出列表res

以图中的二叉树为例,来一步步来展示这个过程:

初始时,当前节点cur = root ,即节点A

图片3

图片4

图片5

图片6

图片7

图片8

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> res;if(!root) return res;stack<TreeNode* > s;TreeNode* cur = root;while(!s.empty() || cur){// 沿着当前节点cur的左分支一直走到底while(cur){s.push(cur);cur = cur->left;}TreeNode* node = s.top();s.pop();res.emplace_back(node->val);cur = node->right;}return res;}
};

三、二叉树的后序遍历

二叉树的后序遍历的记忆法则是“左右根",即先遍历左子树节点,再遍历右子树节点,最后遍历根节点。

以上图为例,后序遍历的结果是【D, E, B, F, G, C, A】

3.1 解题思路:递归

递归是我们实现前中后序遍历最常用的方法。

什么问题可以采用递归求解呢?

需要满足三个条件:

  1. 一个问题的解可以分解为若干个子问题的解;
  2. 这个问题与分解的子问题,除了数据规模不同外,求解思路相同
  3. 存在递归终止条件。

那么在知道一个问题可以采用递归实现之后,如何写出递归代码呢?

关键在于能写出递归公式,找到终止条件。

在二叉树的前序遍历问题上,它的递归公式是:

preorder(node) = preorder(node->left) --> preorder(node->right) —> print node

它的终止条件是:

node 是否为空,为空则返回。

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;preorder(res, root);return res;}void preorder(vector<int>& res, TreeNode* root){if(!root) return;res.emplace_back(root->val);preorder(res, root->left);preorder(res, root->right);}
};

3.2 解题思路:迭代(方法1)

我们在使用迭代法来实现后序遍历的时候,通常有两大类思路。第一类是先得到“根右左”,然后对输出列表反序,即得到“左右根”; 第二类是直接求出“左右根”。

咱们知道,前序遍历是求“根左右”,那么我们可以使用前序遍历的方法,去求得“根右左”。以上图为例,我们求得的“根右左”的结果是【A, C, G, F, B, E, D】。再对这个结果反序,得到【D, E, B, F, G, C, A】。这正是咱们想得到的后序遍历。

本文第二节和第三节介绍的两种迭代方法,都是采用这种思路来实现。最后第四小节,我们会介绍直接求出后序遍历的思路。


在递归方法实现过程中,它的底层是基于系统栈的结构来实现的。因此,我们可以使用栈的数据结构来辅助实现基于迭代方式的后序遍历。

具体思路为:

  • 初始化栈stack,初始化输出列表res
  • 根节点入栈
  • while(栈不为空),在循环体内部:
    • 栈顶元素出栈
    • 栈顶元素添加到输出列表
    • 如果栈顶元素的左子树节点不为空,将左子树节点入栈
    • 如果栈顶元素的右子树节点不为空,将右子树节点入栈
  • 对输出列表res 进行反序
  • 返回输出列表res

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> res;if(!root) return res;stack<TreeNode*> s;s.push(root);while(!s.empty()){TreeNode* node = s.top();s.pop();res.emplace_back(node->val);if(node->left) s.push(node->left);if(node->right) s.push(node->right);}reverse(res.begin(), res.end());return res;}
};

3.3 解题思路:迭代(方法2)

基于迭代方法的第二种思路如下:

  • 初始化栈stack,初始化输出列表res
  • 设置一个变量cur, 表示当前节点。并赋初始值为根节点root
  • while(栈不为空 或者 当前节点cur不为空),在循环体内部:
    • 沿着当前节点的右分支一直走,直到为空。在这个过程中将遍历的节点都入栈,同时添加到输出列表
    • 栈顶元素出栈
    • 更新当前节点cur为栈顶元素的左子树节点。
  • 对输出列表res 进行反序
  • 返回输出列表res

以图中的二叉树为例,来一步步来展示这个过程:

初始时,当前节点cur = root ,即节点A

图片3

图片4

图片5

图片6

图片7

图片8

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> res;if(!root) return res;stack<TreeNode*> s;TreeNode* cur = root;while(!s.empty() || cur){while(cur){res.emplace_back(cur->val);s.push(cur);cur = cur->right;}TreeNode* node = s.top();s.pop();cur = node->left;}reverse(res.begin(), res.end());return res;}
};

3.4 解题思路:迭代(方法3)

这次我们要尝试直接得到后序遍历的结果。

我们知道后序遍历的顺序是”左右根“。那么对于一个根节点,要先遍历它的左子树节点和右子树节点,再回头遍历根节点。

为了防止遍历根节点的时候,再次遍历到它的左、右子树节点;我们需要一个变量,来表示最后一次出栈的元素。

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> res;if(!root) return res;stack<TreeNode* > s;s.push(root);TreeNode* lastPopNode = root;while(!s.empty()){TreeNode* node = s.top();if(node->left && node->left != lastPopNode && node->right != lastPopNode){s.push(node->left);}else if(node->right && node->right != lastPopNode){s.push(node->right);}else{res.emplace_back(node->val);s.pop();lastPopNode = node;}}return res;  }
};

四、二叉树的层序遍历

4.1 解题思路:广度优先搜索BFS

广度优先搜索是从树的根节点开始,沿着树的宽度来遍历树的节点。如果所有节点均被访问,则算法中止。广度优先搜索通常采用队列来辅助实现。

在题目中要求按照层序遍历,实现逐层地从左向右访问所有的结点。这正好符合广度优先搜索的策略。

我们可以立即想到普通广度优先搜索的模板:

vector<int> bfs(TreeNode* root) {std::vector<int> result;if(!root){return result;}   queue<TreeNode*> q;q.push(root);while(!q.empty()){TreeNode* node = q.front();q.pop();result.emplace_back(node->val);if(node->left){q.push(node->left);}            if(node->right){q.push(node->right);}}return result;
}	

但是,这里返回的是一个一维数组,和题目要求的二维数组不一致。因此,我们需要稍作修改,修改内容如下:

  • 在while循环每次遍历时,获取队列的长度N(即队列中的节点个数)
  • 一次性的处理这N个节点,然后再进入下一轮循环。

通过这种修改,确保每次循环时要处理的队列中元素都是本层的元素。

vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> result;if(!root){return result;}queue<TreeNode*> q;q.push(root);while(!q.empty()){vector<int> tmp;// 每次q.size() 获取的都是队列的长度(一个层的节点个数)for(int i = q.size(); i > 0 ; i--){TreeNode* node = q.front();q.pop();tmp.emplace_back(node->val);if(node->left) q.push(node->left);if(node->right) q.push(node->right);}result.emplace_back(tmp);}return result;}

4.2 解题思路:深度优先搜索DFS

本题也可以使用深度优先搜索来实现。

深度优先搜索是采用栈或者递归来实现。(递归在系统内部也是基于栈来实现)

vector<vector<int>> levelOrder(TreeNode* root) {std::vector<std::vector<int>> res;if(!root) return res;dfs(0, root, res);return res;
}// index 是节点所在层的层索引
void dfs(int index, TreeNode* node, std::vector<std::vector<int>>& res){// node 应该放到res[index]中, 如果res[index]元素不存在, 需要先创建一个if(res.size() < index + 1){res.push_back({});}res[index].emplace_back(node->val);if(node->left){dfs(index+1, node->left, res);}if(node->right){dfs(index+1, node->right, res);}
}

后记:
我将以上内容整理成一份PDF文档,需要的朋友可以加我微信好友【xinglunan_0608】找我要一份

在这里插入图片描述

这篇关于二叉树的前序、中序、后序、层序遍历,递归和迭代两大类解题思路,每类细分不同解法【完整版】附PDF文档的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/230339

相关文章

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr