壹 ❀ 引

按照一天一题的速度,不知不觉已经刷了快两多月的leetcode了,因为本人较为笨拙,一道简单的题有时候也会研究很久,看着提交了两百多次,其实也才解决了70来道简单题,对于二分法,双指针等也只是有个初步概念,并非熟练。

若你有注意我以往题解文章,会发现我做过的大多题型均以数组和字符串为主。这是因为我在选择题目的时候始终将自己限制在熟悉的知识体系里,我非常害怕树,害怕递归,害怕动态规划。我深知害怕解决不了问题,自己始终得面对它们,所以今天我决定开始啃树了(学习树形结构不是啃树皮),懦弱的心做出稍微勇敢的决定。不积跬步,无以至千里;不积小流,无以成江海,愿你我共勉,那么本文开始。

贰 ❀ 前序遍历、中序遍历、后序遍历

在学习树之前,我想大家或多或少会联想到深度优先与广度优先相关概念。我在学习树的卡片时,结果又注意到前序遍历,中序遍历,后序遍历以及层序遍历等概念,这一下我就懵了。所以在学树之前,我们先 了解这些常见的搜索规则。

贰 ❀ 壹 前序遍历

前序遍历可以说最符合大家阅读树结构的查询顺序,它满足根节点=>左子树=>右子树的顺序,我们来看个例子:

如上图,其中A为根节点,B为左子树,C为右子树,所以遍历顺序为ABC

我们再看看层级复杂点的二叉树结构,如下:

还是一样的套用上面的规律,首先是根节点A,之后遍历左子树B,而B又有自己的左子树D和右子树E,所以BDE又可以看成一个独立二叉树,因此遍历完B之后,紧接着就是遍历左子树D与右子树E。

E又有自己的左子树和右子树,因此遍历完E紧接着就是GH,到这里左子树完整遍历结束。于是来到右子树C,由于C没有左子树,所以紧接着遍历F,F遍历完又遍历了自己的左子树I,到这里遍历结束。所以完整的顺序为ABDEGHCFI

我们永远先遍历根节点,紧接着判断有没有左子树,如果有接着遍历,而左子树也可以有自己的左子树与右子树,所以我们可以用递归来做到遍历。

来看一道题,题目来源144. 二叉树的前序遍历,题目描述如下:

给定一个二叉树,返回它的 前序 遍历。

示例:

输入: [1,null,2,3]

1
\
2
/
3

输出: [1,2,3]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

让我们尝试实现它:

/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root) {
let res = [];
// 遍历函数
function traversal(root) {
if (root !== null) {
// 访问根节点的值
res.push(root.val);
if (root.left) {
// 递归遍历左子树
traversal(root.left);
};
if (root.right) {
// 递归遍历右子树
traversal(root.right);
};
};
};
traversal(root);
return res;
};

贰 ❀ 贰 中序遍历

中序遍历满足左子树=>根节点=>右子树的顺序进行查询,我们还是以简单二叉树为例。

当跑到到根节点B时,先得看看有没有左子树,正好有,所以先遍历了左子树A之后才是B,最后遍历右子树C,所以完整顺序顺序为ABC

我们再来用中序遍历分析稍微复杂的二叉树,如下图:

从F开始,先看有没有左子树,因为有于是成功跑到了B,注意此时并不是直接取到B,由于B也有左子树,所以第一个遍历到的节点其实是A,而A没有其它分支了,所以紧接着遍历A的根节点B,然后跑到B的右子树D。而D也有自己的左子树与右子树,所以D不能被立刻遍历,而是先遍历C,然后才是D,最后是E。到这里F节点的左子树全部遍历完成,此时顺序为ABCDEF

我们再看F的右子树,由于G没有左子树,所以直接遍历G,然后跑到G的右子树I,但I有左子树,所以先取H,再取I。结合上面,此二叉树的中序遍历顺序为ABCDEFGHI,我是以字母顺序提前排好了遍历顺序,大家陌生就多自己走几遍。

那么如何实现一个中序遍历呢,很简单,将前序遍历代码换个顺序就好了,我们始终先遍历节点的左子树,子又有左子树那就一直往下找,找到底才是当前左子树根节点,最后右子树。

看一道题,题目来自leetcode94. 二叉树的中序遍历,描述如下:

给定一个二叉树,返回它的中序遍历。

示例:

输入: [1,null,2,3]

1
\
2
/
3

输出: [1,2,3]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

/**
* @param {TreeNode} root
* @return {number[]}
*/
var inorderTraversal = function(root) {
let res = [];
// 遍历函数
function traversal(root) {
if (root !== null) {
if (root.left) {
// 递归遍历左子树
traversal(root.left);
};
// 访问根节点的值
res.push(root.val);
if (root.right) {
// 递归遍历右子树
traversal(root.right);
};
};
};
traversal(root);
return res;
};

贰 ❀ 叁 后序遍历

后序遍历满足左子树=>右子树=>根节点的顺序进行查询,还是从简单二叉树开始:

从根节点C出发,先访问左子树A,紧接着右子树B,最后根节点C,所以顺序为ABC

我们再来看一个较为复杂的二叉树,如下:

还是从根节点I出发,于是成功找到了左子树E,而E也有自己的左子树,所以第一个遍历到了A,紧接着来到E的右子树D,但D也有自己的左右子树,所以第二个遍历的是B,紧接着是C,最后是根节点D,根节点E。到这里,左子树遍历完成,顺序为ABCDE

我们再来看I的右子树H,由于H有右子树G,所以H不能被遍历,G也有自己的左子树F,于是遍历到了F,G没右子树,于是F之后就是G,谨记着H,到这里I的右子树遍历完成,最后遍历根节点I。所以完整的顺序为ABCDEFGHI,顺序还是与字母顺序一致,大家跟着思路多多感受。

看到题,题目来自leetcode145. 二叉树的后序遍历,题目描述如下:

给定一个二叉树,返回它的后序遍历。

示例:

输入: [1,null,2,3]

1
\
2
/
3

输出: [1,2,3]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

那么如何实现它呢?还是将上面的实现换个顺序即可。

/**
* @param {TreeNode} root
* @return {number[]}
*/
var postorderTraversal = function(root) {
let res = [];
// 遍历函数
function traversal(root) {
if (root !== null) {
if (root.left) {
// 递归遍历左子树
traversal(root.left);
};
if (root.right) {
// 递归遍历右子树
traversal(root.right);
};
// 访问根节点的值
res.push(root.val);
};
};
traversal(root);
return res;
};

叁 ❀ 层序遍历

层序遍历满足从上到下,从左到右一层一层遍历的顺序,以简单的二叉树为例:

首先从根节点A开始,这是第一层,遍历完成往下一层推进,于是访问到了B和C,完整顺序为ABC

我们再来看一个较为复杂的二叉树,由于层序遍历理解上还是较为容易,下图遍历顺序为ABCDEFGHI,确实是从上到下从左往右一层层往下推进的遍历顺序。

来看一道题,题目来自leetcode102. 二叉树的层序遍历,描述如下:

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例:

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

   3
/ \
9 20
/ \
15 7

返回其层次遍历结果:

[

[3],

[9,20],

[15,7]

]

让我们来实现它,我们每遍历一层,都会将本层节点装到一个数组里,每往下推进一层,我们都得创建一个新的子数组,实现如下:

/**
* @param {TreeNode} root
* @return {number[][]}
*/
var levelOrder = function(root) {
let res = []; function traversal(root, depth) {
if (root !== null) {
if (!res[depth]) {
res[depth] = [];
};
res[depth].push(root.val)
if (root.left) {
traversal(root.left, depth + 1);
};
if (root.right) {
traversal(root.right, depth + 1);
};
};
};
traversal(root, 0);
return res;
};

关于层序遍历我们来趁热打铁,再来补一道我昨天做的题,题目来自leetcode104. 二叉树的最大深度,描述如下:

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:

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

   3
/ \
9 20
/ \
15 7

返回它的最大深度 3 。

注意,由于我们是要统计最大深度,所以第一层就应该是从1开始,这里直接贴代码:

/**
* @param {TreeNode} root
* @return {number}
*/
var maxDepth = function(root) {
let ans = 0;
if(root===null){
return ans;
};
function traversal(root,deepth){
// 始终取最大数为最深层级
ans = Math.max(ans,deepth)
if(root.left){
traversal(root.left,deepth+1);
};
if(root.right){
traversal(root.right,deepth+1);
};
};
// 从1开始
traversal(root,1);
return ans;
};

肆 ❀ 深度优先、广度优先与前序、中序、后序、层序遍历关系

那么我们经常听闻的深度优先DFS与广度优先(宽度优先)BFS又和前面的前序、中序、后序、层序遍历又有何关系呢?我想聪明的同学应该已经联想到了,前序、中序、与后序遍历均为深度优先。而层序遍历也就是所谓的广度优先了。

对比不难发现,深度优先更具钻研精神,只要子树还有子,就一直往下查找,至于顺序对应前中后的查找顺序。而广度是将每一层节点遍历完成为止,才会进入下一层。

伍 ❀ 总

需要说明的是,上述实现代码其实都不是最优做法,部分实现均借鉴了leetcode题解一套拳法刷掉n个遍历树的问题一文。因为对于现在的我来说,最易理解的解答要优于更好的性能,至于性能提升还需要日后积累,那么到这里本文结束。

JS 前序遍历、中序遍历、后序遍历、层序遍历详解,深度优先与广度优先区别,附leetcode例题题解答案的更多相关文章

  1. Java实现二叉树的前序、中序、后序遍历(非递归方法)

      在上一篇博客中,实现了Java中二叉树的三种遍历方式的递归实现,接下来,在此实现Java中非递归实现二叉树的前序.中序.后序遍历,在非递归实现中,借助了栈来帮助实现遍历.前序和中序比较类似,也简单 ...

  2. LeetCode二叉树的前序、中序、后序遍历(递归实现)

    本文用递归算法实现二叉树的前序.中序和后序遍历,提供Java版的基本模板,在模板上稍作修改,即可解决LeetCode144. Binary Tree Preorder Traversal(二叉树前序遍 ...

  3. Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)

      在上一篇博客中,实现了Java中二叉树的四种遍历方式的递归实现,接下来,在此实现Java中非递归实现二叉树的前序.中序.后序.层序遍历,在非递归实现中,借助了栈来帮助实现遍历.前序和中序比较类似, ...

  4. 【2】【leetcode-105,106】 从前序与中序遍历序列构造二叉树,从中序与后序遍历序列构造二叉树

    105. 从前序与中序遍历序列构造二叉树 (没思路,典型记住思路好做) 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [ ...

  5. PHP递归方法实现前序、中序、后序遍历二叉树

    二叉树是每个节点最多有两个子树的树结构.通常子树被称作“左子树”(left subtree)和“右子树”(right subtree). class Node { public $value; pub ...

  6. 二叉树各种相关操作(建立二叉树、前序、中序、后序、求二叉树的深度、查找二叉树节点,层次遍历二叉树等)(C语言版)

    将二叉树相关的操作集中在一个实例里,有助于理解有关二叉树的相关操作: 1.定义树的结构体: typedef struct TreeNode{ int data; struct TreeNode *le ...

  7. 二叉树的前序、中序、后序遍历 python

    话不多说,直接上代码 class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None cl ...

  8. 数据结构实习 - problem K 用前序中序建立二叉树并以层序遍历和后序遍历输出

    用前序中序建立二叉树并以层序遍历和后序遍历输出 writer:pprp 实现过程主要是通过递归,进行分解得到结果 代码如下: #include <iostream> #include &l ...

  9. Python实现二叉树的前序、中序、后序、层次遍历

      有关树的理论部分描述:<数据结构与算法>-4-树与二叉树:   下面代码均基于python实现,包含: 二叉树的前序.中序.后序遍历的递归算法和非递归算法: 层次遍历: 由前序序列.中 ...

  10. 数据结构-二叉树(1)以及前序、中序、后序遍历(python实现)

    上篇文章我们介绍了树的概念,今天我们来介绍一种特殊的树--二叉树,二叉树的应用很广,有很多特性.今天我们一一来为大家介绍. 二叉树 顾名思义,二叉树就是只有两个节点的树,两个节点分别为左节点和右节点, ...

随机推荐

  1. 问题--VSCODE编写C含scanf无终端跳出

    1.问题 在VSCODE中编写C程序,用到scanf输入时,发现无终端输入数据 2.解决方法 在设置里搜索RunInTerminal,勾选该选项即可

  2. 【TouchGFX】MIXINS - ClickListener

    ClickListener Click Listener混合器使它可以应用到其小部件,并能够通过使用回调扩展小部件来响应触摸输入. 在TouchGFX Designer中,可以通过在" Mi ...

  3. [java] JSP post 提交乱码 解决方案

    //在post提交的页面顶部插入下列代码 <% request.setCharacterEncoding("UTF-8"); %>

  4. 云服务器搭建自己的GitServer!

    云服务器搭建自己的GitServer! 如果你有一台云服务器并想在上面搭建自己的 Git 服务器,你可以使用 Git 自带的 git-shell ,也可以使用像 Gitea.GitLab.Gogs 这 ...

  5. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.12.15)

    一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...

  6. [转帖]SQL Server索引的维护 - 索引碎片、填充因子

    https://www.cnblogs.com/kissdodog/archive/2013/06/14/3135412.html 这两个问题都和页密度有关,虽然两者的表现形式在本质上有所区别,但是故 ...

  7. [转帖]ipset详解

    https://zhuanlan.zhihu.com/p/489103374 ipset创建:create 创建一个新的ipset集合:ipset create SETNAME TYPENAME SE ...

  8. [转帖] jq实现json文本对比

      原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介# 近期,为了给一个核心系统减负,组内决定将一些调用量大的查询接口迁移到另一个系统,由于接口逻辑比较复杂,为了保 ...

  9. Redis monitor命令

    MONITOR Syntax MONITOR Available since: 1.0.0 Time complexity: ACL categories: @admin, @slow, @dange ...

  10. [转贴]Kubernetes之修改NodePort对外映射端口范围

    https://www.cnblogs.com/minseo/p/12606326.html k8s默认使用NodePort对外映射端口范围是30000-50000可以通过修改kube-apiserv ...