102. 二叉树的层序遍历递归法:深度优先搜索的巧妙应用

二叉树的层序遍历是一种经典的遍历方式,它要求按层级逐层访问二叉树的节点。通常我们会使用队列来实现层序遍历,但递归法也是一种可行且有趣的思路。本文将深入探讨递归法解决二叉树层序遍历的核心难点,并结合代码和模拟过程进行详细讲解。

一、题目描述

给定一个二叉树的根节点 root,返回其节点值的层序遍历结果,即逐层遍历,从左到右访问所有节点。例如,输入二叉树:

    3/ \9  20/  \15   7

输出结果应为:
[[3], [9, 20], [15, 7]]

二、核心难点分析

难点1:临时数组的创建逻辑

在递归法中,我们需要预先创建好数组来存储每一层的节点值。这里的关键是如何根据当前节点的深度(层级)来确定将节点值存储到哪个数组中。

  • 深度标记
    我们使用一个变量 deep 来表示当前节点所在的深度。初始时,根节点的深度为 1
  • 数组创建与填充
    在递归过程中,每当遇到一个新的深度 deep 时,我们需要确保 res 中已经有足够的子列表来存储该层的节点值。如果 res 的大小小于 deep,则创建一个新的空列表并添加到 res 中。然后,将当前节点的值添加到 res 中对应深度的子列表中。

难点2:递归的具体逻辑实现

递归法的核心在于如何通过递归调用自身来遍历二叉树的每一层。

  • 递归终止条件
    当遇到 null 节点时,递归结束。这是因为 null 节点没有值,也没有子节点,不需要进行任何处理。
  • 递归调用
    对于每个非 null 节点,我们先将其值添加到对应深度的子列表中,然后分别对其左子节点和右子节点进行递归调用。在递归调用时,深度 deep 需要加 1,以表示进入了下一层。

三、解题思路分步解析

步骤1:初始化结果列表和递归调用

public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> res = new ArrayList<>();levelOrderBFS(res, root, 0);return res;
}

这里我们首先创建了一个空的结果列表 res,然后调用递归函数 levelOrderBFS 开始遍历。初始深度设为 0,因为在递归函数内部会先将深度加 1 再进行处理。

步骤2:递归函数实现

public void levelOrderBFS(List<List<Integer>> res, TreeNode root, int deep) {if (root == null) {return;}deep++;while (res.size() < deep) {List<Integer> resList = new ArrayList<>();res.add(resList);}res.get(deep - 1).add(root.val);levelOrderBFS(res, root.left, deep);levelOrderBFS(res, root.right, deep);
}
  • 深度处理
    首先将深度 deep1,因为当前节点的深度比其父节点大 1
  • 数组创建与填充
    使用 while 循环检查 res 的大小是否小于 deep。如果是,则创建一个新的空列表 resList 并添加到 res 中。这确保了 res 中始终有足够的子列表来存储每一层的节点值。
    然后,通过 res.get(deep - 1).add(root.val) 将当前节点的值添加到 res 中对应深度的子列表中。
  • 递归调用
    最后,分别对当前节点的左子节点和右子节点进行递归调用,深度 deep 保持不变。这使得递归能够逐层深入,遍历整个二叉树。

四、递归流程模拟

以二叉树 [3, 9, 20, null, null, 15, 7] 为例,结构如下:

    3/ \9  20/  \15   7

以下是递归过程的详细模拟:

初始状态

res = []deep = 0root = 3

第一次递归调用

  • deep = 1
  • res.size() = 0 < 1,创建新列表 [] 并添加到 res 中,此时 res = [[]]
  • 3 添加到 res[0] 中,此时 res = [[3]]
  • root.left(即 9)进行递归调用,deep = 1
  • root.right(即 20)进行递归调用,deep = 1

9 的递归调用

  • deep = 2
  • res.size() = 1 < 2,创建新列表 [] 并添加到 res 中,此时 res = [[3], []]
  • 9 添加到 res[1] 中,此时 res = [[3], [9]]
  • root.left(即 null)进行递归调用,deep = 2
  • root.right(即 null)进行递归调用,deep = 2

20 的递归调用

  • deep = 2
  • res.size() = 2 == 2,直接将 20 添加到 res[1] 中,此时 res = [[3], [9, 20]]
  • root.left(即 15)进行递归调用,deep = 2
  • root.right(即 7)进行递归调用,deep = 2

15 的递归调用

  • deep = 3
  • res.size() = 2 < 3,创建新列表 [] 并添加到 res 中,此时 res = [[3], [9, 20], []]
  • 15 添加到 res[2] 中,此时 res = [[3], [9, 20], [15]]
  • root.left(即 null)进行递归调用,deep = 3
  • root.right(即 null)进行递归调用,deep = 3

7 的递归调用

  • deep = 3
  • res.size() = 3 == 3,直接将 7 添加到 res[2] 中,此时 res = [[3], [9, 20], [15, 7]]
  • root.left(即 null)进行递归调用,deep = 3
  • root.right(即 null)进行递归调用,deep = 3

最终结果

res = [[3], [9, 20], [15, 7]]

五、关键知识点总结

1. 递归的基本概念

递归是一种解决问题的方法,它通过将问题分解为更小的子问题,并通过调用自身来解决这些子问题。在二叉树的层序遍历中,我们通过递归调用自身来遍历每一层的节点。

2. 深度优先搜索(DFS)与广度优先搜索(BFS)

递归法本质上是一种深度优先搜索(DFS)的应用。与广度优先搜索(BFS)不同,DFS会沿着一条路径一直深入下去,直到无法继续,然后再回溯到上一层,继续探索其他路径。在层序遍历中,我们通过递归实现了一种特殊的DFS,它能够逐层遍历二叉树的节点。

3. 动态数组的使用

在递归过程中,我们使用了一个动态数组 res 来存储每一层的节点值。动态数组的特点是可以根据需要动态地增加或减少元素,这使得我们能够灵活地处理不同层级的节点数量。

六、完整代码

import java.util.ArrayList;
import java.util.List;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val = val; }TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> res = new ArrayList<>();levelOrderBFS(res, root, 0);return res;}public void levelOrderBFS(List<List<Integer>> res, TreeNode root, int deep) {if (root == null) {return;}deep++;while (res.size() < deep) {List<Integer> resList = new ArrayList<>();res.add(resList);}res.get(deep - 1).add(root.val);levelOrderBFS(res, root.left, deep);levelOrderBFS(res, root.right, deep);}
}

七、总结

递归法解决二叉树的层序遍历问题虽然不像队列法那样直观,但它展示了深度优先搜索在树形结构中的巧妙应用。通过理解临时数组的创建逻辑和递归的具体实现,我们能够更好地掌握递归的思想和技巧。在实际应用中,递归法可能在某些情况下比队列法更简洁高效,尤其是对于一些复杂的树形结构或需要深度优先处理的问题。希望本文的讲解能够帮助读者更好地理解和掌握递归法解决二叉树层序遍历的核心难点。

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

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

相关文章

首个窗口级无人机配送VLN系统!中科院LogisticsVLN:基于MLLM实现精准投递

导读 随着智能物流需求日益增长&#xff0c;特别是“最后一公里”配送场景的精细化&#xff0c;传统地面机器人逐渐暴露出适应性差、精度不足等瓶颈。为此&#xff0c;本文提出了LogisticsVLN系统——一个基于多模态大语言模型的无人机视觉语言导航框架&#xff0c;专为窗户级别…

WPF Datagrid 数据加载和性能

这篇文章并非讨论 WPF Datagrid 的性能数据&#xff0c;而只是简单介绍一下为了使其性能良好&#xff0c;你需要注意哪些方面。我不太想使用性能分析器来展示实际数据&#xff0c;而是尽可能地使用了 Stopwatch 类。这篇文章不会深入探讨处理海量数据的技术&#xff0c;例如分页…

matlab求矩阵的逆、行列式、秩、转置

inv - 计算矩阵的逆 用途&#xff1a;计算一个可逆矩阵的逆矩阵。 D [1, 2; 3, 4]; % 定义一个2x2矩阵 D_inv inv(D); % 计算矩阵D的逆 disp(D_inv);det - 计算矩阵的行列式 用途&#xff1a;计算方阵的行列式。 E [1, 2; 3, 4]; determinant det(E); % 计算行列式 disp…

ridecore流水线解读

文章目录 流水线stage分属前后端PCpipelineIFIDDPDP 与 SW 中间没有latchSWCOM 源码地址 流水线stage分属前后端 IF -> ID -> DP -> SW -> EX -> COM分类阶段说明前端IF指令获取阶段。PC 使用分支预测器&#xff0c;访问指令存储器。典型前端操作。前端ID解码并…

【SpringBoot】关于MP使用中配置了数据库表前缀的问题

problem 使用MP时&#xff0c;在application.yml配置文件中配置了MP匹配数据库表中的表名时的前缀作了规定&#xff0c;如下&#xff1a; 那么当我运行时报错了错误&#xff0c;报错信息如下&#xff1a; 因为我数据库表的书类表名是book&#xff0c;MP在匹配时使用了表名前…

印度Rummy游戏支付通道申请策略:技巧类游戏的合规与创新

本文为印度支付申请科普文&#xff0c;自去年开始&#xff0c;印度Rummy类游戏申请印度支付都需要拥有AIGF的会员及产品证书。 如需要rummy可以通过AIGF审核的源。码&#xff0c;或咨询AIGF的相关内容&#xff0c;可以联。系老妙。 印度作为全球棋牌类游戏增长最快的市场之一&…

日志与策略模式

什么是设计模式 IT⾏业 ,为了让 菜鸡们不太拖⼤佬的后腿, 于是⼤佬们针对⼀些经典的常⻅的场景, 给定了⼀些对应的解决⽅案, 这个就是 设计模式 日志认识 计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件&#xff0c;主要作⽤是监控运⾏状态、记录异常信 息&#xff…

解锁Ubuntu高效部署!自动安装配置文件YAML全解析

我们之前介绍了两种Ubuntu系统的安装方式&#xff0c;分别对应桌面版&#xff08;准备搞OpenStack了&#xff0c;先装一台最新的Ubuntu 23.10&#xff09;和服务器版&#xff08;Ubuntu 22.04 LTS服务器版本安装演示&#xff09;。但对于有些用户&#xff0c;因为技术问题&…

关系代数和关系数据库语言(SQL)

阅读提示&#xff1a;本篇文章较长&#xff0c;建议从目录上选取想看的内容。代码上的话&#xff0c;我习惯用小写&#xff0c;如果看不习惯建议跳过。有问题欢迎讨论&#xff01;&#xff01;&#xff01; 一、基础概念 1.1数据库的概念 数据库(Database)是按照数据结构来组…

EXO 可以将 Mac M4 和 Mac Air 连接起来,并通过 Ollama 运行 DeepSeek 模型

EXO 可以将 Mac M4 和 Mac Air 连接起来&#xff0c;并通过 Ollama 运行 DeepSeek 模型。以下是具体实现方法&#xff1a; 1. EXO 的分布式计算能力 EXO 是一个支持 分布式 AI 计算 的开源框架&#xff0c;能够将多台 Mac 设备&#xff08;如 M4 和 Mac Air&#xff09;组合成…

区块链基本理解

文章目录 前言一、什么是分布式账本(DLT)二、什么是P2P网络?二、共识算法三、密码算法前言 区块链是由一个一个数据块组成的链条,按照时间顺序将数据块逐一链接,通过哈希指针链接,所有的数据块共同维护一份分布式账本(DLT),每个节点(可以理解为一个玩家,一台计算机)都拥…

Node.js中的洋葱模型

文章目录 前言 前言 Node.js中的洋葱模型是一种中间件执行机制&#xff0c;主要用于处理HTTP请求和响应的流程控制。该模型通过层层包裹的中间件结构&#xff0c;实现请求从外到内穿透、响应从内向外返回的顺序执行。以下从核心概念、实现原理、框架差异及实际应用等方面解析&…

UI-TARS Desktop:用自然语言操控电脑,AI 重新定义人机交互

在人工智能技术飞速发展的今天,从文本生成到图像识别,AI 的能力边界不断被打破。而字节跳动近期开源的 UI-TARS Desktop,则将这一技术推向了更复杂的交互场景——通过自然语言直接控制计算机界面,实现了图形用户界面(GUI)的智能化自动化。这款工具不仅降低了操作门槛,更…

一个可拖拉实现列表排序的WPF开源控件

从零学习构建一个完整的系统 推荐一个可通过拖拉&#xff0c;来实现列表元素的排序的WPF控件。 项目简介 gong-wpf-dragdrop是一个开源的.NET项目&#xff0c;用于在WPF应用程序中实现拖放功能&#xff0c;可以让开发人员快速、简单的实现拖放的操作功能。 可以在同一控件内…

C语言中字符串函数的详细讲解

C语言提供了丰富的字符串处理函数&#xff0c;这些函数在<string.h>头文件中声明。以下是一些常用字符串函数的详细讲解&#xff1a; 字符串拷贝函数 strcpy 功能&#xff1a;将源字符串&#xff08;包括结尾的\0&#xff09;复制到目标字符串。原型&#xff1a;char *s…

可视化数据图表怎么做?如何实现三维数据可视化?

目录 一、三维数据可视化的要点 1. 明确数据可视化的目标 2. 筛选与整理数据 3. 选择合适的图表类型 4. 运用专业工具制作 5. 优化图表的展示效果 二、数据可视化图表怎么做&#xff1f; 1. 理解三维数据的特性 2. 数据处理与三维建模 3. 设置光照与材质效果 4. 添加…

在Linux服务器上部署Jupyter Notebook并实现ssh无密码远程访问

Jupyter notebook版本7.4.2&#xff08;这个版本AI提示我Jupyter7&#xff08;底层是 jupyter_server 2.x&#xff09; 服务器开启服务 安装Jupyter notebook 7.4.2成功后&#xff0c;终端输入 jupyter notebook --generate-config 这将在 ~/.jupyter/ 目录下生成 jupyter_…

走出 Demo,走向现实:DeepSeek-VL 的多模态工程路线图

目录 一、引言&#xff1a;多模态模型的关键转折点 &#xff08;一&#xff09;当前 LMM 的三个关键挑战 1. 数据的真实性不足 2. 模型设计缺乏场景感知 3. 语言能力与视觉能力难以兼顾 &#xff08;二&#xff09;DeepSeek-VL 的根本出发点&#xff1a;以真实任务为锚点…

数据库原理及其应用 第六次作业

题目 参考答案 题目1. 教材P148第1题 问题&#xff1a;什么是数据库的安全性&#xff1f; 答案&#xff1a;数据库的安全性是指保护数据库以防止不合法的使用所造成的数据泄露、更改或破坏 。它通过用户身份鉴别、存取控制&#xff08;包括自主存取控制和强制存取控制&#x…

2025系统架构师---选择题知识点(押题)

1.《计算机信息系统安全保护等级划分准则》(GB 17859-1999)由低到高定义了五个不同级别的计算机系统安全保护能力。 第一级:用户自主保护级---通过隔离用户与数据实现访问控制,保护用户信息安全; 第二级:系统审计保护级---实施更细粒度的访问控制,通过审计和隔离资源确…