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

news2024/11/25 20:00:16

今天在学习力扣上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],
再遍历右子树,以此类推。
中序遍历完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1021155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue基础之模板语法介绍

前言 上篇我分享了关于Vue的入门,简单的入了个门。本篇文章将要分享的内容为Vue的模板语法。 一、插值 1.1、文本 1.2、html 1.3、属性 1.4、class、style绑定 1.5、表达式 在Vue的模板语法中,插值是一种常用的方式来动态地将数据渲染到视图中。Vue使用双…

CentOS 7 安装Libevent

CentOS 7 安装Libevent 1.下载安装包 新版本是libevent-2.1.12-stable.tar.gz。(如果你的系统已经安装了libevent,可以不用安装) 官网:http://www.monkey.org/~provos/libevent/ 2.创建目录 # mkdir libevent-stable 3.解压 …

数字森林:无人机航测技术在林业调查中的应用

林业调查是林业工作的基础,对于森林资源的管理、规划、保护、经济发展和农业种植等方面都具有重要的意义。传统林业调查主要依赖人工进行,存在工作效率低、数据精度低、数据分析困难、受地形限制、无法实时监测等缺陷。 随着科技的不断发展,无…

DJYOS开源往事三:DJYOS源码发布网络实证

在DJYOS经营开发社区的时候,DJYOS的代码更新记录是在自己的官网上。然后散发到各种技术论坛上。这里我实证的举例以第三方网站为数据源头,罗列2009年之后发布的一些源码实证信息。 1、2009年2月2日:djyos含example的0.2.0版本发布了&#xf…

JDK1.8下载

https://www.oracle.com/cn/java/technologies/downloads/#java8-windows

git log和git reflog命令

工作区 版本历史库 ,暂存区 (1)git log (2)git log --oneline (3)git log -n4 --graph (4)git log -n4 --graph --oneline (5)git log --all (6)git log master 查看master分支历史记录(1)HEAD指向当前工作commit,是个变量 (2)git reset HEAD^,git log找不到之前的commi…

函数式编程------JDK8新特性

函数式编程式jdk 8中的语法糖,在许多地方都有用到,以下是一些优点. 能够看懂公司里的代码大数量下处理集合效率高代码可读性高消灭嵌套地狱 Lamda表达式 lamda表达式是函数是编程的基础,先看一个列子 新建一个线程,参数是匿名类内部类(匿名内部类是一个匿名子类对象。这里使…

Web应用系统的小安全漏洞及相应的攻击方式

写作目的 本文讲述一个简单的利用WebAPI来进行一次基本没有破坏力的“黑客”行为。 主要目的如下: 了解什么叫安全漏洞 知道什么是api 了解一些获取api的工具 通过对API的认识了解白盒接口测试基本概念和技术 免责声明: 本文主要是以学习交流为目的&a…

python抠图(去水印)开源库lama-cleaner入门应用实践

1. 关于 Lama Cleaner Lama Cleaner 是由 SOTA AI 模型提供支持的免费开源图像修复工具。可以从图片中移除任何不需要的物体、缺陷和人,或者擦除并替换(powered by stable diffusion)图片上的任何东西。 特征: 完全免费开源&am…

axios取消上一页面的请求

请求拦截 声明变量用于存放请求请求拦截存放请求导出请求 导航守卫 导入请求集合路由前置守卫中遍历取消所有请求 登录失效取消后续请求

Go学习视频整理(总共59门课程,860GB)

由阿里P8 Golang架构师亲自精心筛选整理的全网最新最具价值的Golang进阶学习课程! 培训机构原版教程! 课程知识点和一线大厂完美匹配! 所有课程资源完整成套,不残缺,不拼凑,不拆开乱发! 这系…

深度学习——线性神经网络一

深度学习——线性神经网络一 文章目录 前言一、线性回归1.1. 线性回归的基本元素1.1.1. 线性模型1.1.2. 损失函数1.1.3. 解析解1.1.4. 随机梯度下降1.1.5. 用模型进行预测 1.2. 向量化加速1.3. 正态分布与平方损失1.4. 从线性回归到深度网络 二、线性回归的从零开始实现2.1. 生…

【每日运维】文件系统损坏:shutting down filesystem

问题场景:服务器突发断电导致 处理步骤 先确认属于哪个逻辑卷:ls -l /dev/mapper 通过文件系统命令进行修复:xfs_repair -L /dev/mapper/centos-root 最终修复的预期效果如下: 重启服务器后验证是否正常进入系统

Polygon L2扩容方案揭秘

1. 引言 前序博客: Polygon生态 以太坊扩容是一个几乎与以太坊本身一样古老的问题。扩容任务的复杂性是 以太坊伟大之处的直接结果: 以太坊是有活力的协议,其缓慢进化,以确保链的安全性和去中心化。 当活动的增加刺激了更大的…

vue3项目学习一:创建vue3项目

创建vue3项目 一、使用vue-cli创建vue3项目1.安装vue-cli2.创建vue3项目 二、初始化项目结构三、导入element-ui 一、使用vue-cli创建vue3项目 1.安装vue-cli 先查看是否安装vue-cli 在cmd窗口输入vue -V查看版本,如果出现 则说明存在vue-cli,如果出现 则需要安…

FTP这么“好用”和“便宜”,为什么企业还要替换掉?

FTP是一种历史悠久的网络协议,自1971年问世以来,它因其简易性、便捷性以及强大的跨平台兼容性而被广泛使用。在网站开发、软件更新和数据备份等多个场景中,FTP都发挥了重要作用。不过,随着互联网技术的不断发展和企业需求的多样化…

DockerCompose和Docker镜像仓库

个人名片: 博主:酒徒ᝰ. 个人简介:沉醉在酒中,借着一股酒劲,去拼搏一个未来。 本篇励志:三人行,必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》,SpringCloud…

SpringCloud Alibaba - Nacos

1.安装与部署 Nacos是阿里巴巴开源的服务注册与发现、配置管理的组件,相当于是EurekaConfig的组合。 Nacos服务器是单独安装部署的,需要下载Nacos服务端程序,下载地址https://github.com/alibaba/nacos。 window下双击startup.cmd 登录Naco…

Linux的Redis集群搭建-主从集群哨兵模式

上次教大家在linux中安装单机版本的redis: Linux安装Redis(图文解说详细版) 这次我们讲一下Linux安装redis的集群版本 文章目录 🌴准备redis环境🌴第一步,下载redis🌴第二步,传输到…

PCIE研究-2

PCIe是一种高速串行总线,用于连接计算机内部的各种设备。在PCIe中,有四种不同的设备类型:Switch、Bridge、Root Complex和EndPoint。本篇文章将介绍这四种设备类型的基础知识。 1. Switch Switch是PCIe中最常见的设备类型之一,它…