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

力扣每日一题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) 额外空间)展开这棵树吗?


面试中遇到过这道题?

1/5

通过次数

465.3K

提交次数

631.8K

通过率

73.6%

结点结构

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/

方法一:前序遍历

前序遍历所有的结点,将遍历的结点依次放入一个数组中,然后再转换成单链表。

class Solution {
public:void preorder(TreeNode *root,vector<TreeNode*> &temp){if(!root) return ;temp.push_back(root);preorder(root->left,temp);preorder(root->right,temp);}void flatten(TreeNode* root) {if(!root) return;vector<TreeNode*> temp;preorder(root,temp);temp.push_back(NULL);for(int i=0;i<temp.size()-1;i++){temp[i]->left=NULL;temp[i]->right=temp[i+1];}}
};

官解还提供了下面两种方法

方法二:前序遍历和展开同时进行

使用方法一的前序遍历,由于将节点展开之后会破坏二叉树的结构而丢失子节点的信息,因此前序遍历和展开为单链表分成了两步。能不能在不丢失子节点的信息的情况下,将前序遍历和展开为单链表同时进行?

之所以会在破坏二叉树的结构之后丢失子节点的信息,是因为在对左子树进行遍历时,没有存储右子节点的信息,在遍历完左子树之后才获得右子节点的信息。只要对前序遍历进行修改,在遍历左子树之前就获得左右子节点的信息,并存入栈内,子节点的信息就不会丢失,就可以将前序遍历和展开为单链表同时进行。

该做法不适用于递归实现的前序遍历,只适用于迭代实现的前序遍历。修改后的前序遍历的具体做法是,每次从栈内弹出一个节点作为当前访问的节点,获得该节点的子节点,如果子节点不为空,则依次将右子节点和左子节点压入栈内(注意入栈顺序)。

展开为单链表的做法是,维护上一个访问的节点 prev,每次访问一个节点时,令当前访问的节点为 curr,将 prev 的左子节点设为 null 以及将 prev 的右子节点设为 curr,然后将 curr 赋值给 prev,进入下一个节点的访问,直到遍历结束。需要注意的是,初始时 prev 为 null,只有在 prev 不为 null 时才能对 prev 的左右子节点进行更新。

class Solution {
public:void flatten(TreeNode* root) {if (root == nullptr) {return;}auto stk = stack<TreeNode*>();stk.push(root);TreeNode *prev = nullptr;while (!stk.empty()) {TreeNode *curr = stk.top(); stk.pop();if (prev != nullptr) {prev->left = nullptr;prev->right = curr;}TreeNode *left = curr->left, *right = curr->right;if (right != nullptr) {stk.push(right);}if (left != nullptr) {stk.push(left);}prev = curr;}}
};

方法三:寻找前驱结点。(类似于Morris遍历)

前两种方法都借助前序遍历,前序遍历过程中需要使用栈存储节点。有没有空间复杂度是 O(1)O(1)O(1) 的做法呢?

注意到前序遍历访问各节点的顺序是根节点、左子树、右子树。如果一个节点的左子节点为空,则该节点不需要进行展开操作。如果一个节点的左子节点不为空,则该节点的左子树中的最后一个节点被访问之后,该节点的右子节点被访问。该节点的左子树中最后一个被访问的节点是左子树中的最右边的节点,也是该节点的前驱节点。因此,问题转化成寻找当前节点的前驱节点。

具体做法是,对于当前节点,如果其左子节点不为空,则在其左子树中找到最右边的节点,作为前驱节点,将当前节点的右子节点赋给前驱节点的右子节点,然后将当前节点的左子节点赋给当前节点的右子节点,并将当前节点的左子节点设为空。对当前节点处理结束后,继续处理链表中的下一个节点,直到所有节点都处理结束。

class Solution {
public:void flatten(TreeNode* root) {TreeNode *curr = root;while (curr != nullptr) {if (curr->left != nullptr) {auto next = curr->left;auto predecessor = next;while (predecessor->right != nullptr) {predecessor = predecessor->right;}predecessor->right = curr->right;curr->left = nullptr;curr->right = next;}curr = curr->right;}}
};

http://www.lryc.cn/news/342330.html

相关文章:

  • Linux系统下使用LVM扩展逻辑卷的步骤指南
  • 探索AI编程新纪元:从零开始的智能编程之旅
  • RustGUI学习(iced)之小部件(三):如何使用下拉列表pick_list?
  • 【OceanBase诊断调优】—— Unit 迁移问题的排查方法
  • [极客大挑战 2019]PHP
  • 数据结构之跳跃表
  • 搜维尔科技:动作捕捉解决方案:销售、服务、培训和支持
  • 数据库管理-第184期 23ai:干掉MongoDB的不一定是另一个JSON数据库(20240507)
  • 刷代码随想录有感(58):二叉树的最近公共祖先
  • [开发|安卓] Android Studio 开发环境配置
  • 开发 Chrome 浏览器插件入门
  • 在数字化转型的浪潮中,CBDB百数服务商如何破浪前行?
  • 程序员的实用神器
  • spss 导入数据的时候 用于确定数据类型的值所在的百分比95%是什么意思,数据分析,医学数据分析
  • Python进阶之-上下文管理器
  • 什么年代了,还在拿考勤说事
  • 泰迪智能科技中职大数据实验室建设(职业院校大数据实验室建设指南)
  • Qt QThreadPool线程池
  • 无人机+三维建模:倾斜摄影技术详解
  • Window(Qt/Vs)软件添加版本信息
  • 工厂模式+策略模式完成多种登录模式的实现
  • 赋能企业数字化转型 - 易点易动固定资产系统与飞书实现协同管理
  • Sectigo 通配符SSL证书的优势分析!
  • nuxt2路由,以及重构以前项目,路由使用
  • eureka报错:链接8761被拒绝
  • Linux 手动部署JDK21 环境
  • 【c2】编译预处理,gdb,makefile,文件,多线程,动静态库
  • c++结构体用构造函数进行初始化
  • 2024年五一数学建模C题完整解题思路代码
  • 0018__GTK+:GTK+的简介、安装、使用方法之详细攻略