【LeetCode 热题 100】114. 二叉树展开为链表——(解法二)分治

Problem: 114. 二叉树展开为链表
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

【LeetCode 热题 100】114. 二叉树展开为链表——(解法一)后序遍历+头插法

文章目录

  • 整体思路
  • 完整代码
  • 时空复杂度
    • 时间复杂度:O(N)
    • 空间复杂度:O(H)

整体思路

这段代码同样旨在解决 “二叉树展开为链表” 的问题,即将二叉树原地转换成一个按前序遍历顺序串联的“链表”。与上一个“后序遍历+头插法”的版本不同,此版本采用的是一种 “分解问题” (Divide and Conquer) 的思想,通过递归函数返回子问题(子树)展开后的尾节点来辅助父问题的解决。

  1. DFS函数的设计

    • 该算法的核心是一个辅助函数 dfs(node),它的设计目标是:
      a. 原地展开node 为根的子树。
      b. 返回这棵子树被展开成链表后的尾节点
  2. 分解与合并逻辑 (Divide and Conquer)

    • 分解 (Divide):对于当前节点 node,问题被分解为两个子问题:展开左子树和展开右子树。这是通过递归调用 leftTail = dfs(node.left)rightTail = dfs(node.right) 来实现的。
      • leftTail 会接收到左子树展开后链表的尾节点。
      • rightTail 会接收到右子树展开后链表的尾节点。
    • 解决 (Conquer):当两个子问题都解决后(即左右子树都已各自拉平),开始处理当前节点 node,将其与两个已拉平的子链表合并。
      • 合并的目标是形成 node -> (左子链表) -> (右子链表) 的结构。
      • if (leftTail != null): 这个判断检查是否存在左子树。
        • leftTail.right = node.right;: 这是最关键的一步。它将左子链表的尾部 (leftTail) 的 right 指针,连接到原先 node 的右子树(现在已经是右子链表的头部)。这就把左右两个链表串起来了。
        • node.right = node.left;: 将 node 的右指针指向其左孩子,即左子链表的头部。
        • node.left = null;: 将 node 的左指针置空。
      • 完成这三步后,以 node 为根的子树已经被成功展开。
  3. 返回尾节点

    • 在合并完成后,需要确定并返回当前整个大链表(node + 左子链表 + 右子链表)的尾节点。
    • 这个尾节点的位置有三种可能:
      a. 如果存在右子树,那么尾节点就是右子树的尾节点 (rightTail)。
      b. 如果不存在右子树但存在左子树,那么尾节点就是左子树的尾节点 (leftTail)。
      c. 如果左右子树都不存在,那么尾节点就是 node 本身。
    • return rightTail != null ? rightTail : leftTail != null ? leftTail : node; 这句三元表达式完美地实现了这个逻辑。

这个算法通过自底向上的方式,在每一层都完成子树的展开和连接,最终将整个树拉平。

完整代码

class Solution {/*** 将给定的二叉树原地展开为链表。* @param root 二叉树的根节点*/public void flatten(TreeNode root) {// 调用辅助的DFS函数,主函数不需要返回值dfs(root);}/*** 辅助DFS函数:原地展开以 node 为根的子树,并返回展开后链表的尾节点。* @param node 当前子树的根节点* @return 展开后链表的尾节点*/private TreeNode dfs(TreeNode node) {// 基线条件:如果节点为空,则没有什么可展开的,返回 null。if (node == null) {return null;}// 1. 分解:递归地展开左右子树,并获取它们展开后的尾节点。TreeNode leftTail = dfs(node.left);   // 展开左子树,获取左链表的尾TreeNode rightTail = dfs(node.right); // 展开右子树,获取右链表的尾// 2. 解决/合并:处理当前节点 node,将其与已展开的左右子链表连接。// 如果存在左子树(即左子链表),才需要进行连接操作。if (leftTail != null) {// a. 将左链表的尾部的 right 指针,指向右子链表的头部 (node.right)。leftTail.right = node.right;// b. 将 node 的右指针指向左子链表的头部 (node.left)。node.right = node.left;// c. 将 node 的左指针置空。node.left = null;}// 3. 返回当前已展开大链表的尾节点。// 优先级:右子树的尾 > 左子树的尾 > 当前节点本身。if (rightTail != null) {return rightTail;}if (leftTail != null) {return leftTail;}return node;}
}

时空复杂度

时间复杂度:O(N)

  1. 节点访问:该算法通过DFS递归遍历了树中的每一个节点。每个节点被访问和处理一次。
  2. 操作:在每个节点上,执行的都是常数时间的指针操作。
  3. 综合分析
    • 如果树中有 N 个节点,dfs 函数会被调用 N 次。
    • 因此,总的时间复杂度与节点数成正比,即 O(N)

空间复杂度:O(H)

  1. 主要存储开销:算法是原地修改,没有创建新的数据结构来存储节点。主要的额外空间开销来自于 递归调用栈
  2. 空间大小:递归调用的深度取决于树的高度 H
    • 在最好的情况下(一个完全二叉树),树的高度 H 约为 log N,空间复杂度为 O(log N)。
    • 在最坏的情况下(一个极度倾斜的链状树),树的高度 H 等于 N,此时递归栈的深度也为 N,空间复杂度为 O(N)

综合分析
算法的辅助空间复杂度主要由递归栈的深度决定,即树的高度 H。因此,空间复杂度为 O(H)。在最坏情况下,H 可能为 N,所以最坏空间复杂度也可以表述为 O(N)

参考灵神

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

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

相关文章

【WPF】WPF 自定义控件 实战详解,含命令实现

🧩《WPF 自定义控件》实战详解本文将围绕如何编写一个自定义控件(如带右键菜单的图片控件 ImageView),逐步讲解其定义、命令绑定与 ContextMenu 中常见的语法技巧。🧱 一、创建一个 WPF 自定义控件的步骤 WPF 中自定义…

Flink 2.0 DataStream算子全景

在实时流处理中,Apache Flink的DataStream API算子是构建流处理 pipeline 的基础单元。本文基于Flink 2.0,聚焦算子的核心概念、分类及高级特性。 一、算子核心概念:流处理的"原子操作 1. 数据流拓扑(Stream Topology&#x…

Flask 入门到实战(2):使用 SQLAlchemy 打造可持久化的数据层

Flask 入门到实战:使用 SQLAlchemy 打造可持久化的数据层一、前言:为什么用 Flask-SQLAlchemy? 在 Python Web 开发中,操作数据库的方式主要有两种: 直接写 SQL(繁琐且难维护)使用 ORM&#xff…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | GithubProfies(GitHub 个人资料)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— GithubProfies组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 使用 Vue 3 的 Composition API&#xff08;<script setup…

simscape中坐标系和坐标变换Frames and Transforms

为了更便捷地描述单个物体的运动&#xff0c;最好以该物体的质心为坐标原点建立坐标系&#xff0c;从而可以非常方便地描述其旋转运动。因此&#xff0c;在计算多个物体之间的位置关系时&#xff0c;为了计算方便&#xff0c;需要频繁地更换坐标框架&#xff0c;这也是multibod…

构建分布式光伏“四可”能力:支撑新型电力系统安全稳定运行的关键路径

随着我国新能源装机规模的跨越式增长&#xff0c;国家能源战略对新能源电站的规范化接入与精细化调度管理提出了更高要求。在电力市场化改革深化与新型电力系统构建的关键时期&#xff0c;保障电网安全稳定、提升新能源高效消纳能力已成为核心议题。国家能源局于2025年1月17日正…

UART寄存器介绍

在 STM32 微控制器中&#xff0c;UART&#xff08;通用异步收发传输器&#xff09;通信通过多个寄存器实现配置和数据传输。下面详细解析 UART 的核心寄存器及其功能。1. 状态寄存器&#xff08;USART_SR&#xff09;状态寄存器反映 UART 当前的工作状态&#xff0c;用于判断数…

写一个算法对一组值进行归一化映射,使它们在视觉上有明显的区分度,尤其在数据集分布不均时仍能体现差异

问题&#xff1a; 有一批数据&#xff0c;都是随机值范围是不确定&#xff0c;我需要用这个值来绘制同样数量圆&#xff0c;不同值他们的圆半径不同&#xff0c;考虑到数据有时候大小偏差不大&#xff0c;这1000个值有可能是集中在10,20之间&#xff0c;也可能是分布广泛&#…

具身智能零碎知识点(五):VAE中对使用KL散度的理解

VAE中对使用KL散度的理解什么是 VAE (Variational AutoEncoder)&#xff1f;从自编码器 (AE) 说起VAE&#xff1a;让潜在空间变得“有意义”和“连续”KL 散度是如何用到的&#xff1f;通俗理解 KL 散度在 VAE 中的作用&#xff1a;带来的好处&#xff1a;KL 散度公式 (无需背诵…

理解:进程、线程、协程

线程、进程和协程是并发编程的重要组成部分。进程&#xff08;Process&#xff09;定义进程是操作系统分配资源的基本单位&#xff0c;表示一个正在执行的程序。一旦一个程序被加载到内存中&#xff0c;它就成为一个进程&#xff0c;而每个进程都有其独立的内存空间。特征进程之…

总结一下找素数的三种方法

目录 一试除法 二埃氏筛 三线性筛(欧拉筛) 一试除法 思想&#xff1a;就是判断某个数x是不是素数,就判断从2开始到小于根号x的范围内有没有能够取余不等于0的,这个说明当前值就是x的一个因子&#xff0c;所以不是素数。 代码&#xff1a; import java.util.Scanner;public…

基于Yolov8车辆检测及图像处理系统【有代码】

0 引言 随着城市化进程的加速和机动车保有量的快速增长,交通管理、智能监控和自动驾驶等领域对车辆目标检测技术的需求日益增长。车辆目标检测是计算机视觉领域的一个重要研究方向,其目标是从图像或视频序列中准确识别和定位车辆,为后续的车辆跟踪、行为分析和交通流量统计…

MySQL密码管理器“mysql_config_editor“

目录 核心能力 常用命令速查 为什么更安全&#xff1f; 典型场景 mysql_config_editor 是 MySQL 官方自带的一款命令行小工具&#xff0c;作用一句话&#xff1a;把账号、密码、主机、端口等连接信息加密存起来&#xff0c;下次连接时只敲一个名字即可&#xff0c;不用再写…

Kubernetes高级调度01

目录 第一章&#xff1a;初始化容器&#xff08;InitContainer&#xff09;—— 应用启动前的 “准备军” 1.1 InitContainer 的基本概念与核心特性 1.2 InitContainer 与普通容器的关键区别 1.3 InitContainer 的实战场景与示例解析 1.3.1 示例 1&#xff1a;延迟启动 —…

LSV负载均衡

什么是访问压力&#xff1f;--负载 两个客户同时访问一个服务器&#xff0c;会导致服务器崩溃调度---Cluster集群&#xff08;为了解决一个特定问题&#xff0c;多台服务器组合使用形成的一个系统&#xff09;LSV 1、集群Cluster LB&#xff1a;负载均衡&#xff0c;有多个主机…

复习笔记 38

绪论 其实没有一种安稳快乐&#xff0c;永远也不差 专题 2 知识点 继续学数学强化吧&#xff1f;可以。还有概率论要学。还有高数后半部分的数一专项要学。还有政治要学。要学的内容确实还是挺多的啊。加油。下载了一个阅读的软件&#xff0c;可以做一做真题的阅读理解。政治英…

GaussDB like 的用法

1 like 作用在 where 子句中使用 like 运算符来搜索列中的指定模式。 有两个通配符与 like 运算符一起使用&#xff1a;&#xff05; - 百分号表示零个&#xff0c;一个或多个字符 _ - 下划线表示单个字符注&#xff1a;也同时支持正则表达式。2 like 语法select column1, colu…

单例模式:确保全局唯一实例

单例模式确保一个类只有一个实例&#xff0c;并提供全局访问点。适用于需要全局唯一对象的场景&#xff08;如配置管理器、数据库连接池&#xff09;。代码示例&#xff1a;import java.util.stream.IntStream;public class ConfigManager {public static void main(String[] a…

深入理解 QSettings:Qt 中的应用程序配置管理

在开发 Qt 应用程序时&#xff0c;管理应用程序的配置信息是一个常见的需求。无论是保存用户的偏好设置、窗口大小&#xff0c;还是应用程序的运行时配置&#xff0c;都需要一种高效且灵活的方式来存储和检索这些信息。Qt 提供了一个强大的工具——QSettings&#xff0c;它能够…

基于SpringBoot+Vue的体育馆预约管理系统(支付宝沙盒支付、腾讯地图API、协同过滤算法、可视化配置、可视化预约)

“ &#x1f388;系统亮点&#xff1a;支付宝沙盒支付、腾讯地图API、协同过滤算法、可视化配置、可视化预约”01系统开发工具与环境搭建—前后端分离架构 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17前端&#xff1a; 技术&#xff1a;框架Vue.js&am…