从理解js双重递归执行顺序到用递归方式实现二叉树中序遍历

2023-09-19 00:26:54

今天在学习力扣上94题二叉树的中序遍历时,js的实现方法之一是递归,但是函数内递归是双重,花了一些时间来理解双重递归调用的执行顺序。
先看如下例子,参考文章(双递归的执行过程理解
示例代码如下:

	const fn = (n) => {
	    if (n > 0) {
           console.log('n1====', n)
           fn(n - 1)
           console.log('n2====', n)
           fn(n - 2)
           console.log('n3====', n)
        }
	}
    fn(3)

我们可以先考虑一下输出的结果是什么呢?
答案揭晓如下:

// n1==== 3
// n1==== 2
// n1==== 1
// n2==== 1
// n3==== 1
// n2==== 2
// n3==== 2
// n2==== 3
// n1==== 1
// n2==== 1
// n3==== 1
// n3==== 3

一开始看到这个结果,真是两眼一黑,什么鬼!那我们先不急,一步步来分析~
1、首先注意第一次递归调用:首次调用时,n=3,所以输出结果n1 = 3是毫无疑问的,接着执行fn(n - 1),因为n - 1 = 2 > 0,所以执行第一次递归没问题吧

2、接着就是一个知识点:进栈
众所周知,js是单线程,fn函数内均为同步执行,所以第一次递归之后,n=3进栈,执行fn(n - 1)的n=2操作,而我把fn(n - 1)执行的过程称为进栈(压栈)。如下图:
在这里插入图片描述
3是最先进栈,所以在栈底。
注意:此时fn(n - 1)以下的代码片段全部未执行。
输出结果为:

n1==== 3
n1==== 2
n1==== 1

当n = 1时,执行fn(n - 1),则n = 0,即if (n > 0)失效。当前递归结束,开始释放栈内存(出栈),而栈是后进先出结构,如下图:
在这里插入图片描述

3、接上图,n=1 出栈,同步执行以下代码片段

	console.log('n2====', n)
    fn(n - 2)
    console.log('n3====', n)

所以第四行打印出 n2==== 1
接着执行fn(n - 2),因当前n=1,不符合表达式n>0情况,所以本次递归调用不执行。
接着输出打印 n3==== 1,此时到输出的打印结果第五行。

4、n=1 出栈后,2出栈:
重复上一步,输出:

n2==== 2
n3==== 2

此时到打印结果第7行。

5、n=2 出栈后,3出栈:
输出结果n2==== 3
执行递归fn(n - 2),此时 if 表达式 n = 1 > 0 成立,则从头执行。

注意:此时n = 3进栈,最后一行代码console.log('n3====', n)不执行

执行fn(3 - 2)后:
首先输出n1==== 1,再执行fn(n - 1),表达式不成立,接着向下执行,
输出n2==== 1,向下执行fn(n - 2),表达式不成立,准备出栈,继续向下执行,
输出n3==== 1
最后!注意:之前执行fn(n - 2)时有n = 3进栈!所以此时同步流程结束,需要出栈:
输出最后一个结果n3==== 3
至此,双递归调用流程结束。

// =================================

到这,终于明白双递归是怎么被调用执行的了!~
接下来,我们来看怎么用递归的方式实现二叉树中序遍历:
中序遍历返回顺序是左 => 根/中 => 右,如下图:
在这里插入图片描述
按照中序遍历输出的结果应该是 [4, 2, 5, 1, 6, 3, 7]。

依照上面例子给我们的经验,要获取左子树的最后一个左节点的值再进行操作,那我们可以进行压栈,当左子树的某个左节点left为null时,代表当前节点为最后节点,数据结构大致如下:

const tree = {
    val: '1',
    left: {
        val: '2',
        left: { val: '4', left: null, right: null },
        right: { val: '5', left: null, right: null }
    },
    right: {
        val: '3',
        left: { val: '6', left: null, right: null },
        right: { val: '7', left: null, right: null }
    },
}

代码实现如下:

var inorderTraversal = function (root) {
    let arr = []
    const fn = (tree) => {
        if (!tree) return
        fn(tree.left)
        arr.push(tree.val)
        fn(tree.right)
    }
    fn(root)
    return arr
};

console.log(inorderTraversal(tree))

如上,执行fn(tree.left)时,一直在压栈,一直到 { val: '4', left: null, right: null },这一层,执行结束,释放栈,此时栈如下(看val值):
在这里插入图片描述
4首先出栈,执行arr.push(tree.val)后,执行递归fn(tree.right),val=4的这一层,right = null,被return,本次执行结束。
2再次出栈,再执行递归fn(tree.right),成立,arr此时为[4, 2, 5].
1出栈,执行后,arr = [4, 2, 5, 1],
再遍历右子树,以此类推。
中序遍历完成。

更多推荐

单点登录原理及JWT实现

单点登录原理及JWT实现一、单点登录效果首先我们看通过一个具体的案例来加深对单点登录的理解。案例地址:https://gitee.com/xuxueli0323/xxl-sso?_from=gitee_search把案例代码直接导入到IDEA中然后分别修改下server和samples中的配置信息在host文件中配置1

【C语言】结构体内存对齐机制详解

目录一、前言二、结构体内存对齐规则三、实例解析一、前言在讲解结构体内存对齐机制之前,我们先来看1个例子:typedefstruct{charsex;//性别intid;//学号charname[20];//姓名floatscore;//成绩charaddr[30];//地址}STU;intmain(){STUstude

LeetCode 27. 移除元素(JavaScript 简单)

1.题目给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。说明:为什么返回数值是整数,但输出的答案是数组呢?请注意,输入数组是以「引用」

使用延迟队列解决分布式事务问题——以订单未支付过期,解锁库存为例

目录一、前言二、库存三、订单一、前言上一篇使用springcloud-seata解决分布式事务问题-2PC模式我们说到了使用springcloud-seata解决分布式的缺点——不适用于高并发场景因此我们使用延迟队列来解决分布式事务问题,即使用柔性事务-可靠消息-最终一致性方案(异步确保型)以下是下订单的代码//@Gl

ctfshow web入门(1)

web1查看页面源代码web2ctr+uweb3因为查看源码没有东西,网络查看下数据包,找到flagweb4robots协议其他都没啥信息,就看下robots.txt,这个文件可能会泄露部分网站目录访问下,看到了web5phps泄露也没啥信息,在响应头里面看到了X-Powered-By:PHP/7.3.11得知-网站是

Grom 如何解决 SQL 注入问题

什么是SQL注入SQL注入是一种常见的数据库攻击手段,SQL注入漏洞也是网络世界中最普遍的漏洞之一。SQL注入就是恶意用户通过在表单中填写包含SQL关键字的数据来使数据库执行非常规代码的过程。这个问题的来源就是,SQL数据库的操作是通过SQL命令执行的,无论是执行代码还是数据项都必须卸载SQL语句中,这就导致如果我们在

【操作系统笔记十二】Linux常用基础命令

Linux常用快捷键Tab命令或路径等的补全键,特别常用的快捷键Ctrl+insert复制命令行内容(常用可提高效率)Shift+insert粘贴命令行内容(常用可提高效率)Ctrl+C中断当前任务(退出)Ctrl+Z暂停当前任务Ctrl+I清除屏幕所有的内容Ctrl+A光标迅速回到行首Ctrl+E光标迅速回到行尾Ct

红 黑 树

文章目录一、红黑树的概念二、红黑树的实现1.红黑树的存储结构2.红黑树的插入一、红黑树的概念在AVL树中删除一个结点,旋转可能要持续到根结点,此时效率较低红黑树也是一种二叉搜索树,通过在每个结点中增加一个位置来存储红色或黑色,并对结点的着色进行限制,使得该二叉搜索树的最长路径不超过最短路径的两倍,即红黑树是一颗近似平衡

【postgresql】ERROR: column “xxxx.id“ must appear in the GROUP BY

org.postgresql.util.PSQLException:ERROR:column"xxx.id"mustappearintheGROUPBYclauseorbeusedinanaggregatefunction错误:列“XXXX.id”必须出现在GROUPBY子句中或在聚合函数中使用在mysql中是正常使用

【八大经典排序算法】冒泡排序

【八大经典排序算法】冒泡排序一、概述二、思路解读三、代码实现四、优化一、概述冒泡排序由于其简单和易于理解,使其成为初学者学习排序算法的首选,也是初学者接触到的第一个排序算法。其原理是通过重复交换相邻的元素来将最大的元素逐步“冒泡”到最后。冒泡排序由美国计算机科学家冯·诺伊曼(JohnvonNeumann)于1945年提

使用singularity本地部署wandb

1.背景:wandbself-host(本地部署)官网只支持docker,而不支持singularity,但是现在部分高校或者企业在集群上完全使用singularity替代docker(原因:docker可以挂载任意目录,而容器内是root权限,导致容器外对文件设置的权限,在容器内完全是无用的,因为root用户可以访问

热文推荐