二叉树进阶练习

news2024/11/27 19:58:00

目录

一、根据二叉树创建字符串

二、二叉树的最近公共祖先

三、二叉搜索树与双向链表

四、从前序与中序遍历序列构造二叉树

五、从中序与后序遍历序列构造二叉树

六、二叉树的前序遍历(非递归实现)

七、二叉树的中序遍历(非递归实现)

八、二叉树的后序遍历(非递归实现)


 


一、根据二叉树创建字符串

总结

  1. 左、右子树都为空 --> 不递归遍历左、右子树

  2. 左子树为空,右子树不为空 --> 递归遍历左、右子树

  3. 左子树不为空,右子树为空 --> 只递归遍历左子树

  4. 左、右子树都不为空 --> 递归遍历左、右子树

即当左子树不为空或者右子树不为空时,递归遍历左子树;仅当右子树不为空时,才递归遍历右子树

class Solution {
public:
    string tree2str(TreeNode* root) {
        if (root == nullptr)
            return "";
​
        string str = to_string(root->val);
        // 当左子树不为空或者右子树不为空时,递归遍历左子树
        if (root->left || root->right)
        {
            str += "(";
            str += tree2str(root->left);
            str += ")";
        }
        // 仅当右子树不为空时,才递归遍历右子树
        if (root->right)
        {
            str += "(";
            str += tree2str(root->right);
            str += ")";
        }
        return str;
    }
};


二、二叉树的最近公共祖先

方法一

因为一个节点也可以是它自己的祖先,所以首先判断当前根节点 root 是否等于节点 p 或 q,即判断 p 或 q 是否就是它们的最近公共祖先。

如果不是,则判断 p 和 q 是否分别在 root 的左、右子树中。对于一棵二叉搜索树,可以通过节点的值确定 p 和 q 是在 root 的左子树中,还是右子树中;但对于一棵普通二叉树,只能通过查找的方式确定。

  1. 如果 p 和 q 都在 root 的左子树中,则 p 和 q 的最近公共祖先应该在 root 的左子树中;

  2. 如果 p 和 q 都在 root 的右子树中,则 p 和 q 的最近公共祖先应该在 root 的右子树中;

  3. 如果 p 和 q 分别在 root 的左、右子树中,则 root 就是 p 和 q 的公共祖先。

class Solution {
public:
    bool search(TreeNode* root, TreeNode* x) {
        if (root == nullptr)
            return false;
        return root == x || search(root->left, x) || search(root->right, x); 
    }
​
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == p || root == q)  // 一个节点也可以是它自己的祖先
            return root;
​
        bool pInLeft, pInRight, qInLeft, qInRight;
        pInLeft = search(root->left, p);
        pInRight = !pInLeft;
        qInLeft = search(root->left, q);
        qInRight = !qInLeft;
​
        if (pInLeft && qInLeft)
            return lowestCommonAncestor(root->left, p, q);
        else if (pInRight && qInRight)
            return lowestCommonAncestor(root->right, p, q);
        else
            return root;
    }
};

方法二

如果二叉树的存储结构为三叉链表,即节点结构中增加了一个指向其双亲结点的指针域,那么该问题就可以转化成 相交链表。

如果二叉树的存储结构为二叉链表,我们也可以沿着上述的思路解决该问题,例如:

class Solution {
public:
    // 找到 root 到 x 的路径
    bool FindPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path) {
        if (root == nullptr)
            return false;
​
        path.push(root);
        if (root == x)
            return true;
        
        if (FindPath(root->left, x, path))
            return true;
        
        if (FindPath(root->right, x, path))
            return true;
        
        path.pop();
        return false;
    }
​
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath, qPath;
        FindPath(root, p, pPath);
        FindPath(root, q, qPath);
​
        while (pPath.size() > qPath.size())
        {
            pPath.pop();
        }
        while (qPath.size() > pPath.size())
        {
            qPath.pop();
        }
​
        while (pPath.top() != qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }
        return pPath.top();
    }
};


三、二叉搜索树与双向链表

class Solution {
public:
    void InOrder(TreeNode* cur, TreeNode*& prev) {
        if (cur == nullptr)
            return;
​
        InOrder(cur->left, prev);
​
        cur->left = prev;
        if (prev)
            prev->right = cur;
        prev = cur;
​
        InOrder(cur->right, prev);
    }
​
    TreeNode* Convert(TreeNode* pRootOfTree) {
        if (pRootOfTree == nullptr)
            return nullptr;
​
        TreeNode* prev = nullptr;
        InOrder(pRootOfTree, prev);
​
        TreeNode* phead = pRootOfTree;
        while (phead->left)
        {
            phead = phead->left;
        }
        return phead;
    }
};


四、从前序与中序遍历序列构造二叉树

  1. 在先序序列中,第一个节点一定是二叉树的根节点。

  2. 根据根节点可以将中序序列分割成两个子序列,前一个子序列是根节点的左子树的中序序列,后一个子序列是根节点的右子树的中序序列。

  3. 根据这两个子序列,可以在先序序列中找到对应的左子序列和右子序列。在先序序列中,左子序列的第一个节点是左子树的根节点,右子序列的第一个根节点是右子树的根节点,这样就确定了二叉树的三个节点。同时,左子树和右子树的根节点又可以把左子序列和右子序列划分成两个子序列,如此递归下去,当取尽先序序列中的根节点时,便可得到一棵二叉树。

class Solution {
public:
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,
        int L1, int R1, int L2, int R2)
    {
        // 先序序列的第一个节点一定是根节点
        TreeNode* root = new TreeNode(preorder[L1]);
        // 找到根节点在中序序列中的位置
        int pos = L2;
        while (pos <= R2)
        {
            if (preorder[L1] == inorder[pos])
                break;
            ++pos;
        }
        // 判断左子树是否存在
        if (pos > L2)
        {
            // 递归构造左子树
            root->left = _buildTree(preorder, inorder,
                L1 + 1, L1 + pos - L2, L2, pos - 1);
        }
        // 判断右子树是否存在
        if (pos < R2)
        {
            // 递归构造右子树
            root->right = _buildTree(preorder, inorder,
                R1 - (R2 - pos) + 1, R1, pos + 1, R2);
        }
        return root;
    }
​
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
        return _buildTree(preorder, inorder,
            0, preorder.size() - 1, 0, inorder.size() - 1);
    }
};


五、从中序与后序遍历序列构造二叉树

class Solution {
public:
    TreeNode* _buildTree(vector<int>& postorder, vector<int>& inorder,
        int L1, int R1, int L2, int R2)
    {
        // 后序序列的最后一个节点一定是根节点
        TreeNode* root = new TreeNode(postorder[R1]);
        // 找到根节点在中序序列中的位置
        int pos = L2;
        while (pos <= R2)
        {
            if (postorder[R1] == inorder[pos])
                break;
            ++pos;
        }
        // 判断左子树是否存在
        if (pos > L2)
        {
            // 递归构造左子树
            root->left = _buildTree(postorder, inorder,
                L1, L1 + pos - L2 - 1, L2, pos - 1);
        }
        // 判断右子树是否存在
        if (pos < R2)
        {
            // 递归构造右子树
            root->right = _buildTree(postorder, inorder,
                R1 - (R2 - pos), R1 - 1, pos + 1, R2);
        }
        return root;
    }
​
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        return _buildTree(postorder, inorder,
            0, postorder.size() - 1, 0, inorder.size() - 1);
    }
};


六、二叉树的前序遍历(非递归实现)

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        vector<int> v;
        while (cur || !st.empty())
        {
            while (cur)
            {
                v.push_back(cur->val);
                st.push(cur);
                cur = cur->left;
            }
​
            TreeNode* top = st.top();
            st.pop();
            cur = top->right;
        }
        return v;
    }
};


七、二叉树的中序遍历(非递归实现)

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        vector<int> v;
        while (cur || !st.empty())
        {
            while (cur)
            {
                st.push(cur);
                cur = cur->left;
            }
​
            TreeNode* top = st.top();
            st.pop();
            v.push_back(top->val);
            cur = top->right;
        }
        return v;
    }
};


八、二叉树的后序遍历(非递归实现)

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* prev = nullptr;
        TreeNode* cur = root;
        vector<int> v;
        while (cur || !st.empty())
        {
            while (cur)
            {
                st.push(cur);
                cur = cur->left; 
            }
​
            TreeNode* top = st.top();
            // top 的右子树为空,或者
            // 上一次访问完的节点是 top 的右子树的根节点,
            // 说明 top 的右子树已经遍历过了
            if (top->right == nullptr || top->right == prev)
            {
                st.pop();
                v.push_back(top->val);
                prev = top;
            }
            else
            {
                cur = top->right;
            }
        }
        return v;
    }
};

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

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

相关文章

免杀对抗-Python-混淆算法+反序列化-打包生成器-Pyinstall

Python-MSF/CS生成shellcode-上线 cs上线 1.生成shellcode-c或者python 2.打开pycharm工具&#xff0c;创建一个py文件&#xff0c;将原生态执行代码复制进去 shellcode执行代码&#xff1a; import ctypesfrom django.contrib.gis import ptr#cs#shellcodebytearray(b"生…

RocketMQ整体架构及NameServer源码分析

RocketMQ源码深入剖析 1 RocketMQ介绍 RocketMQ 是阿里巴巴集团基于高可用分布式集群技术&#xff0c;自主研发的云正式商用的专业消息中间件&#xff0c;既可为分布式应用系统提供异步解耦和削峰填谷的能力&#xff0c;同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠…

无涯教程-JavaScript - ISLOGICAL函数

描述 如果指定的值或表达式的计算输出为逻辑值,即True或False,则ISLOGICAL函数将返回逻辑值TRUE。否则返回FALSE。 语法 ISLOGICAL (value)争论 Argument描述Required/OptionalvalueValue or expression.Required Notes 您可以在执行计算之前使用此功能测试单元格的内容。…

《机器学习核心算法》分类算法 - 朴素贝叶斯 MultinomialNB

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 朴素贝叶斯 1、朴素贝叶斯API2、朴素贝叶斯算法实际应用2.1、获取数据集2.2、划分…

【译】怎样修改 HashMap 的 Key?

原文地址&#xff1a;https://www.baeldung.com/java-hashmap-modify-key 1. 概述 在 Java 中&#xff0c;HashMap 是一个广泛使用的数据结构&#xff0c;它以键值对的形式存储元素&#xff0c;提供快速的数据访问和检索。有时&#xff0c;在使用 HashMap 时&#xff0c;我们…

【算法题】1222. 可以攻击国王的皇后

题目&#xff1a; 在一个 8x8 的棋盘上&#xff0c;放置着若干「黑皇后」和一个「白国王」。 给定一个由整数坐标组成的数组 queens &#xff0c;表示黑皇后的位置&#xff1b;以及一对坐标 king &#xff0c;表示白国王的位置&#xff0c;返回所有可以攻击国王的皇后的坐标(…

重启人生,重新出发

Hello, World! 我是EarlGrey&#xff0c;这是时隔两年多后在公众号第一次发文。也许有些老朋友已经忘记我了&#xff0c;也有少数朋友是最近才开始关注的&#xff0c;所以谈正题之前&#xff0c;我先花点时间介绍一下自己。 我是谁 我是英语专业出身&#xff0c;从北外高翻毕业…

使用 SimPowerSystems 的混合技术风电场的无功功率管理(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

什么是 JxBrowser

什么是 JxBrowser 文章目录 什么是 JxBrowser如何使用 JxBrowser容易集成支持的平台Java丰富的文档如何运行主要功能值得信赖成熟的专业技术团队及时的支持与帮助参考资料 JxBrowser 是一个商业跨平台 Java 库&#xff0c;可以让您将基于 Chromium 的网页浏览器控件集成到您的 …

【ES实战】ES中关于segment的小结

文章目录 ES中关于segment的小结ES中segment相关的原理在Lucene中的产生segment的过程。&#xff08;Lucene commit过程&#xff09;ES为了实现近实时可查询做了哪些缩短数据可被搜索的等待时长增加数据的可靠性优化segment的数量 段合并自动合并强制合并 相关配置translog合并…

Python 学习之路 03 之循环

&#x1f600;前言 欢迎来到 Python 循环和流程控制的基础教程&#xff01;无论您是一名新手&#xff0c;还是希望复习 Python 编程的基本知识&#xff0c;这个教程都是一个非常好的资源。在这份教程中&#xff0c;我们将探索 Python 中的不同循环结构和流程控制机制&#xff0…

火山引擎DataWind产品可视化能力揭秘

引言 BI是商业智能(Business Intelligence)的缩写&#xff0c;是一种将企业中现有的数据进行有效的整合的平台&#xff0c;它可以帮助企业、组织和个人更好地了解其业务状况、发现问题&#xff0c;并进行决策。BI产品普遍采用可视化的方式&#xff0c;可以帮助用户更直观、更高…

快速傅里叶变换

引言 目标 傅里叶变化&#xff08;Fourier transform&#xff09;是一种信号处理技术&#xff0c;它可以将时间信号转换为频率信号&#xff0c;即将一组具有相同数量频率的正弦波叠加在一起&#xff0c;形成一组新的正弦波。如果我们把时间信号从频域转换到时域&#xff0c;那么…

Drupal __ 8.5.0 __ XSS文件上传 __CVE-2019-6341

Drupal __ 8.5.0 __ XSS文件上传 __CVE-2019-6341 说明内容漏洞编号CVE-2019-6341漏洞名称Drupal XSS漏洞漏洞评级中危影响范围在7.65之前的Drupal 7版本中&#xff1b; 8.6.13之前的Drupal 8.6版本; 8.5.14之前的Drupal 8.5版本。漏洞描述Drupal诞生于2000年&#xff0c;是一…

PbootCMS在搭建网站

1、打开网站 https://www.pbootcms.com/ 2、点击 “本站” 下载最新的网站代码 3、在本地laragon/www下创建目录&#xff08;hejuwuye&#xff09;&#xff0c;并将代码放进去 4、创建本地数据库&#xff0c;数据库名称为&#xff1a; hejuwuye&#xff0c;然后将static/bac…

25.Xaml DateGrid控件---->默认单选,可以多项选择的网格控件

1.运行效果 2.运行源码 a.Xaml源码 <Window x:Class="testView.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mic…

深入理解JVM虚拟机第五篇:一些常用的JVM虚拟机(二)

文章目录 一&#xff1a;JRockit VM的介绍 二&#xff1a;J9 VM的介绍 三&#xff1a;KVM和CDC/CLDC Hotspot 四&#xff1a;Azul VM的介绍 五&#xff1a;Liquid VM的介绍 六&#xff1a;Apache Harmoney 七&#xff1a;Microsoft JVM 八&#xff1a;Taobao JVM 九&a…

第三方软件测试机构有哪些测试服务软件测试报告收费标准是怎样的?

软件验收机构 一、什么是第三方软件测试机构? 第三方软件测试机构是区别于软件开发公司以及软件需求方的第三方机构&#xff0c;软件企业将软件测试外包给第三方软件测试机构已经成为了行业发展趋势。既省心省力&#xff0c;又降低企业成本&#xff0c;得出的软件测试结果以…

【Proteus仿真】【STM32单片机】四驱寻迹避障小车

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 系统运行后&#xff0c;LCD1602显示红外、超声波检测状态和距离、小车运行状态。可通过K1键可手动切换模式&#xff0c;寻迹、避障、蓝牙遥控&#xff1b;也可通过蓝牙发送指令切换模式&#xff1b; 当处…

绝佳用户体验:构建响应式网页设计的关键原则

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 当谈到前端开发时&#…