什么?二叉树的反前序遍历?

简介: 什么?二叉树的反前序遍历?

       前面几篇文章分享了如何非递归的前中后序遍历,其实掌握前中后序遍历已经可以解决90%的题了,当然你肯定想学的更多。

       先看leetcode的一个场景。


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

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

示例 1:

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

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

      看完题目,是不是想着先前序遍历一遍,将所有节点存下来,然后改变一下节点的指针指向,相信大部分人的思路也是这样(代码后面给出),先说另一个思路,它不是要前序遍历的顺序构造一个链表树么,那我们反前序遍历,将根节点存储下来,依次递归回溯,就得到了题目的答案。

       代码非常的简洁,不过需要深刻理解这个递归过程。

class Solution {
public:
    TreeNode* preNode; // 用于记录上一个处理过的节点的指针
    void flatten(TreeNode* root) {
        if (root == NULL) return; // 终止条件:当节点为空时,直接返回
        flatten(root->right); // 先对右子树进行递归调用,保证右子树已经被拉平
        flatten(root->left); // 再对左子树进行递归调用,保证左子树已经被拉平
        root->left = NULL; // 将当前节点的左子树置为空
        root->right = preNode; // 将当前节点的右子树指向上一个处理过的节点
        preNode = root; // 更新preNode为当前节点,以备下一次递归处理
        // 递归调用到这里时,当前节点已经处理完毕,它的左子树和右子树都已经被拉平并且连接好了
    }
};

      之前思路的代码:

class Solution {
public:
    vector<int> res; // 用于存储中序遍历结果的容器
 
    void inorder(TreeNode *root) {
        stack<TreeNode *> s;
        s.push(root);
        while (root != nullptr || s.size() > 0) {
            while (root != nullptr) {
                TreeNode *cur = s.top();
                s.pop();
                res.push_back(cur->val); // 将当前节点的值保存到结果容器中
                if (cur->right != nullptr) s.push(cur->right); // 将右子节点入栈
                if (cur->left != nullptr) s.push(cur->left); // 将左子节点入栈
            }
        }
    }
    
    void flatten(TreeNode* root) {
        inorder(root); // 对二叉树进行中序遍历,得到展开后的顺序
        TreeNode *r = new TreeNode(-1);
        TreeNode *next = r;
        for (int i = 0; i < res.size(); i++) {
            TreeNode *temp = new TreeNode(res[i]); // 依次创建节点
            next->right = temp; // 将节点接到结果链表的后面
            next = temp;
        }
        root = r->right; // 更新根节点为展开后的链表
    }
};

leetcode官方比较有意思的题解


寻找前驱节点


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


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

class Solution {
public:
    void flatten(TreeNode* root) {
        TreeNode *curr = root; // 当前节点指针,用于遍历二叉树的每个节点
        while (curr != nullptr) {
            if (curr->left != nullptr) { // 如果当前节点的左子节点存在
                auto next = curr->left; // 将当前节点的左子节点保存为next
                auto predecessor = next; // 用predecessor指针指向next,用于找到next的前驱节点
                while (predecessor->right != nullptr) {
                    predecessor = predecessor->right; // 找到next的右子树的最右边节点,即next的前驱节点
                }
                predecessor->right = curr->right; // 将next的前驱节点的右子节点指向当前节点的右子树
                curr->left = nullptr; // 将当前节点的左子节点置为空
                curr->right = next; // 将当前节点的右子节点设置为next
            }
            curr = curr->right; // 移动当前节点指针,继续遍历下一个节点
        }
    }
};
相关文章
|
Web App开发 JavaScript iOS开发
[√]使用vscode开发油猴Tampermonkey脚本
[√]使用vscode开发油猴Tampermonkey脚本
2039 0
|
内存技术
STM32F103 五个时钟源
STM32F103 五个时钟源
926 0
|
Kubernetes 网络性能优化 调度
聊聊 K8S pod 的 QoS(Quality Of Service)
聊聊 K8S pod 的 QoS(Quality Of Service)
|
8月前
|
存储 Oracle 安全
推荐的开源软件中小企业行业数字化场景
在数字化转型中,中小企业常遇成本、技术和安全挑战。Websoft9依托开源软件,提供自主可控的解决方案。文章以十大核心场景为例,如制造业用Odoo ERP降低成本、K3s优化产线监控;金融领域以PostgreSQL替代Oracle节省费用;医疗行业借助Nextcloud保障数据隐私;零售业通过WordPress搭建独立站减少开支等,结合真实案例解析开源技术如何驱动业务创新与增长。
249 0
|
11月前
|
SQL Java 数据库连接
【潜意识Java】深入理解MyBatis,从基础到高级的深度细节应用
本文详细介绍了MyBatis,一个轻量级的Java持久化框架。内容涵盖MyBatis的基本概念、配置与环境搭建、基础操作(如创建实体类、Mapper接口及映射文件)以及CRUD操作的实现。此外,还深入探讨了高级特性,包括动态SQL和缓存机制。通过代码示例,帮助开发者更好地掌握MyBatis的使用技巧,提升数据库操作效率。总结部分强调了MyBatis的优势及其在实际开发中的应用价值。
300 1
|
算法 API 数据安全/隐私保护
LabVIEW编程LabVIEW开发 控制雷赛运动控制器SMC604A例程与相关资料
LabVIEW编程LabVIEW开发 控制雷赛运动控制器SMC604A例程与相关资料
231 0
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
324 2
|
数据库 数据库管理
【异常解决】svn报“Previous operation has not finished; run ‘cleanup‘ if it was interrupted”的错误解决方案
【异常解决】svn报“Previous operation has not finished; run ‘cleanup‘ if it was interrupted”的错误解决方案
1438 0
Springboot最全权限集成Redis-前后端分离-springsecurity-jwt-Token3
Springboot最全权限集成Redis-前后端分离-springsecurity-jwt-Token3
246 72