当前位置: 首页 > news >正文

Leetcode 48

1 题目

114. 二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例 1:

输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [0]
输出:[0]

提示:

  • 树中结点数在范围 [0, 2000] 内
  • -100 <= Node.val <= 100

进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗?

2 代码实现

c

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
typedef struct TreeNode TreeNode ;
void flatten(struct TreeNode* root) {if (root == NULL){return;}flatten(root -> left);flatten(root -> right);TreeNode *left = root -> left ;TreeNode *right = root -> right ;root -> left = NULL ;root -> right = left ;TreeNode *p = root ;while( p->right != NULL){p = p -> right;}p -> right = right;}

cpp

class Solution {
public:// 定义:将以 root 为根的树拉平为链表void flatten(TreeNode* root) {// base caseif (root == nullptr) return;// 利用定义,把左右子树拉平flatten(root->left);flatten(root->right);// *** 后序遍历位置 ***// 1、左右子树已经被拉平成一条链表TreeNode* left = root->left;TreeNode* right = root->right;// 2、将左子树作为右子树root->left = nullptr;root->right = left;// 3、将原先的右子树接到当前右子树的末端TreeNode* p = root;while (p->right != nullptr) {p = p->right;}p->right = right;}
};

这道题是要把一棵二叉树 "拉平" 成一个链表,要求链表的顺序和二叉树的前序遍历结果一致(根节点 -> 左子树 -> 右子树)。我们来一步步拆解这个解法的思路和执行过程。

核心思路

这个解法用后序遍历的思路解决问题:

  1. 先把左子树拉平成链表
  2. 再把右子树拉平成链表
  3. 最后把左子树链表接到根节点右边,再把原右子树链表接到左子树链表的末端

详细步骤拆解

假设我们有这样一棵二叉树:

    1/ \2   5/ \   \
3   4   6
步骤 1:明确函数定义

flatten(TreeNode* root) 的作用是:将以 root 为根的二叉树,拉平成一个链表(链表中节点的 right 指针指向 "下一个节点",left 指针为 null)。

步骤 2:base case

如果root == nullptr(空树),直接返回,不需要任何操作。

步骤 3:递归处理左右子树

先递归处理左子树,再递归处理右子树:

flatten(root->left);  // 把左子树拉平
flatten(root->right); // 把右子树拉平
  • 对左子树(节点 2)递归后,左子树会变成:2 -> 3 -> 4(每个节点的 left 为 null,right 指向下一个)
  • 对右子树(节点 5)递归后,右子树会变成:5 -> 6
步骤 4:拼接链表(后序遍历位置的关键操作)

此时左右子树已经都是链表了,接下来要把它们接到根节点后面:

① 保存左右子树的头节点
TreeNode* left = root->left;  // 左子树链表的头节点(2)
TreeNode* right = root->right; // 右子树链表的头节点(5)
② 将左子树链表接到根节点的右边
root->left = nullptr;    // 左指针置空(链表没有左孩子)
root->right = left;      // 根节点的右指针指向左子树链表的头(2)

此时树变成:

1 -> 2 -> 3 -> 4(原左子树链表)
原右子树5->6暂时游离
③ 将原右子树链表接到当前右子树的末端

需要先找到当前右子树(即原左子树链表)的最后一个节点(4),再把原右子树链表接上去:

TreeNode* p = root;
while (p->right != nullptr) {  // 循环找到当前右子树的最后一个节点p = p->right;
}
p->right = right;  // 最后一个节点的右指针指向原右子树链表的头(5)

最终结果:

1 -> 2 -> 3 -> 4 -> 5 -> 6

为什么用后序遍历?

因为拉平的操作(拼接链表)必须在左右子树都已经拉平之后才能进行。就像搭积木,必须先把左右两块积木各自拼好,才能把它们接到主体上。

总结执行流程

  1. 递归到最底层的叶子节点,先把它们的左右子树(空)处理完
  2. 从叶子节点开始,逐层向上拼接:左子树链表 -> 接到根节点右边 -> 原右子树链表 -> 接到左子树链表末端
  3. 最终整棵树被拉成一个符合前序遍历顺序的链表

通过这种 "先解决子问题,再合并子问题结果" 的思路,高效地完成了二叉树到链表的转换。

【直接说明白为啥这个flatten能实现比较困难,feel一下】

确实,第一次看这个 flatten 函数会觉得 “绕”,难在它的逻辑是 “反直觉” 的 —— 明明要实现 “前序遍历顺序” 的链表,却用了 “后序遍历” 的执行顺序,而且整个过程像在 “拆了东墙补西墙”,得靠 “感觉” 慢慢悟。

可以试着这样 “感受” 它的过程:

想象你手里有一棵二叉树的模型,每个节点都有左右两根 “树枝”(左子树、右子树)。你的目标是把这棵树掰成一条直线(链表),顺序必须是 “根→左→右”,而且只能用右树枝连接(左树枝要掰断)。

怎么掰?你不能直接从根开始硬掰,因为左子树和右子树还没整理好,硬掰会乱。所以你得先把 “左子树” 掰成一条直线(递归处理左子树),再把 “右子树” 也掰成一条直线(递归处理右子树)。这两步就像先把两个 “小乱麻” 捋顺,变成两根 “直绳”。

等左右都捋顺了,就到了最关键的一步:

  1. 把根节点的左树枝掰断(左指针置空),把左子树那根 “直绳” 接到根的右树枝上(根的右指针指向左子树)。
  2. 然后顺着这根刚接好的右绳往下滑(循环找右子树末端),滑到最底端后,再把右子树那根 “直绳” 接上去。

整个过程就像:先把左右两个 “局部” 整理好,再把它们按顺序 “挂” 到根节点后面。你不用操心每一步具体怎么拆,只需要相信 “递归能把小的子树整理好”,最后专注于 “拼接” 就行。

这种 “先处理子问题,再合并结果” 的思路,有点像拼积木:先把零件各自拼好,再按顺序组装成整体。

一开始会觉得 “反着来”,但多看几遍具体例子的拼接过程,慢慢就会有 “哦,原来它是这么一步步把树‘捋直’的” 这种感觉了。

http://www.dtcms.com/a/581952.html

相关文章:

  • 因需写了个内网运维专用的IP地址管理工具,有点不同
  • 沈阳seo整站优化网页设计大师
  • 瓯北网站制作公司怎么用思维导图做网站结构图
  • 【代码随想录算法训练营——Day61】图论——97.小明逛公园、127.骑士的攻击
  • [Dify 实战] 使用 System Prompt 实现企业级策略管控
  • JVM架构与执行流程详解
  • 02 Prompt组件及使用技巧
  • 做网站能用微软企业公示信息年报
  • 本地赣州网站建设网站建设数据库选择
  • 多维竞争时代:手游ASO优化核心策略与安卓商店差异化
  • Linux命令-e2label命令(设置第二扩展文件系统的卷标)
  • 企业AI开发什么工具好?从“可用”到“好用”的全生命周期管理指南
  • (Linux)ELF格式与库的链接原理
  • 如何做网站的营销网站技术防护建设
  • C++新特性 chr类型编码
  • 指纹浏览器模拟功能的实践体验分享
  • C++ 设计模式《订单的撤销和重做》
  • 国网法治建设网站阿里巴巴推广平台
  • 【AI学习-comfyUI学习-SDXL 风格化提示词节点包(Style Prompt Node Pack) 工作流-各个部分学习-第四节】
  • 怎样建设有价值的网站天津建设工程信息网中标公告
  • 提升大语言模型性能的关键技术清单(from 网络)
  • 【NLP】Penn Treebank 与 Parsing:让计算机看懂句子结构
  • Go 1.25 发布:性能、工具与生态的全面进化
  • 北京市保障性住房建设投资中心网站淮南吧
  • Duilib_CEF桌面软件实战之Duilib编译与第一个界面程序
  • MFC动态加载图片
  • Niagara Launcher 全新Android桌面启动器!给手机换个门面!
  • 【Vue】自定义指令之权限控制
  • asp.net网站第一次运行慢网站建设合同书保密条款
  • ZYNQ-7000双核协处理实战:ARM Cortex-A9与FPGA的智能数据采集系统