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

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

       前面几篇文章分享了如何非递归的前中后序遍历,其实掌握前中后序遍历已经可以解决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; // 移动当前节点指针,继续遍历下一个节点
        }
    }
};
相关文章
|
存储 缓存 Java
Elasticesearch内存详解(四)——indexing buffer
介绍elasticsearch的indexing buffer
1789 0
Elasticesearch内存详解(四)——indexing buffer
|
XML 前端开发 Java
SpringMVC入门到实战------2、SpringMVC创建实例Hello SpringMVC(maven+tomcat)
这篇文章是SpringMVC框架的入门教程,详细指导了如何在IDEA中使用Maven和Tomcat创建SpringMVC工程,包括添加依赖、配置web.xml、编写控制器、创建配置文件、配置Tomcat服务器以及进行基本的测试,展示了一个简单的Hello SpringMVC示例。
SpringMVC入门到实战------2、SpringMVC创建实例Hello SpringMVC(maven+tomcat)
|
11月前
|
Android开发
复杂项目即时通讯从android 5升级android x后遗症之解决报错#79 java.io.EOFException Unexpected end of ZLIB input stream-优雅草卓伊凡|bigniu
复杂项目即时通讯从android 5升级android x后遗症之解决报错#79 java.io.EOFException Unexpected end of ZLIB input stream-优雅草卓伊凡|bigniu
619 4
复杂项目即时通讯从android 5升级android x后遗症之解决报错#79 java.io.EOFException Unexpected end of ZLIB input stream-优雅草卓伊凡|bigniu
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
688 1
|
存储 缓存 算法
G1原理—3.G1是如何提升垃圾回收效率
本文深入探讨了G1垃圾回收器提升GC效率的核心机制,包括记忆集(RSet)、位图(BitMap)和卡表(CardTable)的设计与作用。记忆集通过记录跨代引用避免了不必要的老年代遍历,位图用于高效描述内存使用状态以优化标记过程,而卡表则在节约记忆集内存的同时提供更详细的引用信息。此外,文章还解析了DCQ(Dirty Card Queue)和DCQS(Dirty Card Queue Set)机制如何异步更新RSet,确保在高并发场景下的性能与准确性。这些设计共同提升了G1在标记、清理及整理内存时的效率。
841 10
|
人工智能 自然语言处理 语音技术
Baichuan-Audio:端到端音频大模型,实时双语对话+语音生成
Baichuan-Audio 是百川智能推出的端到端音频大语言模型,支持无缝集成音频理解和生成功能,实现高质量、可控的实时中英双语对话。
1653 3
|
人工智能 自然语言处理 搜索推荐
13.5K Star!支持5国语言+全栈语音生成,这个开源AI语音项目绝了!
CosyVoice是由FunAudioLLM团队开发的多语言大语音生成模型,支持中文、英语、日语、韩语和粤语等5种语言。该项目提供从推理、训练到部署的全栈能力,具备零样本语音克隆、跨语言合成、指令控制等前沿功能。其技术架构包括底层模型、多语言支持、框架支持及部方案等,性能优越,RTF&lt;0.2,GPU内存&lt;4GB,QPS&gt;20。相比同类项目,CosyVoice在语言支持、特色功能和部署难度上表现出色,支持本地部署保障数据隐私,并大幅降低商业方案成本。适用于自媒体创作、在线教育、游戏开发、智能硬件和影视制作等多种场景。
1694 0
|
存储 前端开发 Java
Spring MVC中@SessionAttributes注解使用详解
Spring MVC中@SessionAttributes注解使用详解
538 0
|
数据库 数据库管理
【异常解决】svn报“Previous operation has not finished; run ‘cleanup‘ if it was interrupted”的错误解决方案
【异常解决】svn报“Previous operation has not finished; run ‘cleanup‘ if it was interrupted”的错误解决方案
2050 0
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?