【C++】二叉搜索树经典OJ题目

news2024/9/24 3:29:59

文章目录

  • 根据二叉树创建字符串
  • 二叉树的层序遍历
  • 二叉树的层序遍历II
  • 二叉树的最近公共祖先
  • 二叉搜索树与双向链表
  • 从前序与中序遍历序列构造二叉树
  • 从中序与后序遍历序列构造二叉树
  • 二叉树的前序遍历(非递归)
  • 二叉树的中序遍历(非递归)
  • 二叉树的后序遍历(非递归)

根据二叉树创建字符串

在这里插入图片描述

解题思路

这道题是让我们使用前序遍历的方式来创建字符串,但是有一点需要注意的是,再创建的字符串中,需要将每一个左右子树用括号括起来。这里扩括号的时候有两点需要注意的细节:

  • 当左右子树都为空时,该节点左右子树的括号都可以省略掉。
  • 当左子树不为空,右子树为空时,省略掉右子树的括号。
  • 当左子树为空,右子树不为空时,左子树的括号不能省略掉。

代码实现

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;
    }
};

在这里插入图片描述


二叉树的层序遍历

在这里插入图片描述

解题思路

首先,定义一个队列q、记录当前层节点个数的变量levelnum vector <vector < int > >来存储遍历后的结果。 然后判断根节点是不是空, 如果不是空先将根节点入队列,然后levelnum置为1。

外层循环控制队列是否为空,如果为空,则说明节点已经全部出队。该树也已经被访问完了。内层循环来控制每一层节点的个数,然后在队列中按照对头——对尾的顺序依次访问队列中的每一个节点,每访问一个节点,就让该节点出队,并让该节点的左右非空孩子节点入队,这样一来,上一层的节点全部出队后,下一层的节点也就全部入队了。内层循环每循环完一次,就将新队列中的元素个数赋给levelnum 。这样每一层的节点就被依次插入二维数组中的每一行中了。

代码实现

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vv;
        queue<TreeNode*> q;
        int levelnum = 0;
        if(root){
            q.push(root);
            levelnum = 1;
        }
        while(!q.empty())
        {
            vector<int> v;
            while(levelnum--)
            {
                TreeNode* tmp = q.front();
                v.push_back(tmp->val);
                q.pop();
                if(tmp->left)
                    q.push(tmp->left);
                if(tmp->right)
                    q.push(tmp->right);
            }
            vv.push_back(v);
            levelnum = q.size();
        }
        return vv;
    }
};

在这里插入图片描述


二叉树的层序遍历II

在这里插入图片描述

解题思路

这道题目和上一道题的不同点在于:上一道题目是从上到下依次遍历每一层的元素,这道题是从下到上依次遍历每一层。所以对于这道题目,我们只需要将上一道题目的代码拷贝过来,然后将结果逆置一下就可以了。

代码实现

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> q;
        vector<vector<int>> vv;
        int levelSize = 0;
        if(root)
        {
            q.push(root);
            levelSize = 1;
        }
        while(!q.empty())
        {
            vector<int> v;
            while(levelSize--)
            {
                TreeNode* tmp = q.front();
                v.push_back(tmp->val);
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
                q.pop();

            }
            vv.push_back(v);
            levelSize = q.size();
        }
        return vv;
    }
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> vv = levelOrder(root);
        reverse(vv.begin(),vv.end());
        return vv;
    }
};

在这里插入图片描述


二叉树的最近公共祖先

在这里插入图片描述

解题思路

先定义两个栈,用来存储从根节点到p从根节点到q的路径,将他们的路径中的每一个节点存储到栈中。我们需要找的就是这两条路径中相同的那个节点。也就是我们需要先控制栈中的元素个数相同,然后再比较栈顶的元素是否相同,如果不相同则同时弹出两个栈中栈顶的元素,直到两个栈中栈顶的元素相同,此时栈顶的元素就是我们要找的公共祖先。

代码实现

class Solution {
public:
    //定义两个栈,找到两个节点的路径,压入栈中
    bool GetPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
    {
        //节点为空,直接返回false
        if(root == nullptr)
            return false;
        //节点不为空
        path.push(root);
        if(root == x)
            return true;
        if(GetPath(root->left,x,path))
            return true;
        if(GetPath(root->right,x,path))
            return true;
        path.pop();
        return false;        
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath,qPath;
        GetPath(root,p,pPath);
        GetPath(root,q,qPath);
        while(pPath.size() != qPath.size())
        {
            if(pPath.size() > qPath.size())
                pPath.pop();
            else
                qPath.pop();
        }
        while(pPath.top() != qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }
        return pPath.top();
    }
};

在这里插入图片描述


二叉搜索树与双向链表

在这里插入图片描述

解题思路

当前节点的指针用cur表示,再定义一个prev指针,让prev来表示当前节点cur的前一个结点,这样通过不断递归的方式访问每一个节点之后就可以将二叉树转换成双向循环链表了。这里我们需要注意的是,prev一定要使用引用传参,因为每一层节点的prev都是上一次函数调用的cur。

代码实现

class Solution {
public:
    void InOrderConvert(Node* cur, Node* &prev)
	{
		if(cur == nullptr)
			return;
		InOrderConvert(cur->left, prev);
		cur->left = prev;
		if(prev)
			prev->right = cur;
		prev = cur;
		InOrderConvert(cur->right, prev);
	}
    Node* treeToDoublyList(Node* root) {
        if(root == nullptr) return nullptr;
        Node* prev = nullptr;
		InOrderConvert(root, prev);

		Node* cur = root;
		while(cur && cur->left)
		{
			cur = cur->left;
		}
        Node* Rcur = root;
        while(Rcur && Rcur->right)
        {
            Rcur = Rcur->right;
        }
        cur->left = Rcur;
        Rcur->right = cur;
		return cur;
    }
};

在这里插入图片描述


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

在这里插入图片描述

解题思路

大思路很简单,但是这道题目写起来却不是很容易,通过前序我们可以找到根节点,中序分割出左右子树,如果区间还存在说明还有节点未构建完,如果区间不存在说明已经没有节点可以构建,直接返回空。

代码实现

class Solution {
public:
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int begin,int end) 
    {
        if(begin > end)
            return nullptr;
        //创建根节点
        TreeNode* newnode = new TreeNode(preorder[prei]);
        
        //分割左右子区间
        int rooti = begin;
        while(rooti <= end)
        {
            if(inorder[rooti] == preorder[prei])
                break;
            else 
                rooti++;
        }
        prei++;
        //递归创建左右子树
        newnode->left = _buildTree(preorder, inorder, prei, begin, rooti - 1);
        newnode->right = _buildTree(preorder, inorder, prei, rooti + 1, end);
        return newnode;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int i = 0;
        return _buildTree(preorder, inorder, i, 0, inorder.size() - 1);
    }
};

在这里插入图片描述


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

在这里插入图片描述

解题思路

因为后续序列和前序序列有一点点不同,所以这里我们必须从后往前依次访问后序序列中的每一个元素,这样才能找出每一棵子树的根节点并从中序将左右子区间分割开。这里和前序不一样的是,这里我们需要先构建右子树,在构建左子树,最后将根节点和左右子树链接起来。

代码实现

class Solution {
public:
    TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int &pos, int begin, int end)
    {
        if(begin > end)
            return nullptr;
        TreeNode* newnode = new TreeNode(postorder[pos]);
        int rooti = begin;
        while(rooti <= end)
        {
            if(postorder[pos] == inorder[rooti])
                break;
            else
                rooti++;
        }
        pos--;
        //分割左右子区间

        newnode->right = _buildTree(inorder, postorder, pos, rooti + 1, end);
        newnode->left = _buildTree(inorder, postorder, pos, begin, rooti - 1);
        return newnode;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int i = postorder.size() - 1;
        return _buildTree(inorder, postorder, i, 0, postorder.size() - 1);
    }
};

在这里插入图片描述


二叉树的前序遍历(非递归)

在这里插入图片描述

解题思路

前序遍历的顺序是根、左子树、右子树。因此最后我们遍历整棵树的时候,一定是先访问左路节点,然后访问左路节点的右子树,左路节点的右子树又可以看作一棵二叉树、然后继续二叉子树的前序遍历,最后直到遍历访问整棵二叉树。

我们需要定义一个栈st、一个vector v 和一个 TreeNode* curcur初始化为root。定义一个while循环,循环结束的条件是cur不为空,或者st不为空,首相我们先将左路节点依次插入栈中,同时也将左路节点插入到vector中,这时左路节点就已经访问完了,然后将栈顶元素保存下来并出栈,并将cur指向栈顶元素的右子树,做像上面一样的操作。这样就能保证每棵子树都能够按照根——左子树——右子树的顺序访问每一个节点,最后遍历整棵树就能达到预期的结果。

代码实现

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        TreeNode* cur = root;
        vector<int> v;
        stack<TreeNode*> st;
        while(cur || !st.empty())
        {
            while(cur)
            {
                v.push_back(cur->val);
                st.push(cur);
                cur = cur->left;
            }
            //访问每个根节点的右子树
            TreeNode* tmp = st.top();
            st.pop();
            cur = tmp->right;
        }
        return v;
    }
};

在这里插入图片描述


二叉树的中序遍历(非递归)

在这里插入图片描述

解题思路

中序遍历的思路和前序遍历的思路也差不多,有一点不同的就是中序遍历的顺序是左子树——根——右子树,和前序遍历一样,先将根节点入栈,不同的是入栈的同时不能访问节点,等到左路节点全部入栈后,先将该节点保存,然后访问该节点并将该节点弹出栈,然后将cur指向该节点的右子树。然后while循环中重复和前序遍历一样的操作。

代码实现

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

在这里插入图片描述


二叉树的后序遍历(非递归)

在这里插入图片描述

解题思路

后序遍历的顺序是左子树——右子树——根,所以我们在访问根节点的时候必须保证左子树和右子树都已经被访问过了才行。同样,和上面的思路一样,我们同样是先将左路节点入栈并且不访问,当左路节点全部入栈后。取栈顶元素,这时我们需要先判断该节点是否有右子树,或者该节点的右子树是否被访问过。判断该节点的右子树是否被访问过的方法是:设置一个指针 prev 来记录前一个访问过的节点,因为只有右子树访问完才能访问根节点,所以我们只需要判断该节点的右子树是否是上一个访问过的节点就可以了。 同时,如果右子树为空也能直接访问该节点。

代码实现

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

在这里插入图片描述


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

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

相关文章

捷报连连丨小匠物联SILA第六届“智光杯”荣获两项跨界大奖

2023年4月26日&#xff0c;SILA第六届“智光杯”跨界奖项名单公布。 喜讯传来&#xff0c;小匠物联荣获SILA第六届“智光杯”跨界奖项-全屋智能及商用系统优秀新供应链奖、智能照明新锐优秀新供应链奖。 “智光杯”“智光杯”由上海浦东智能照明联合会&#xff08;SILA&#xf…

【校招VIP】简历上项目名称看起来不重复,是安全相关项目,但是为什么简历通过率还是低?

在简历指导的直播里面&#xff0c;我看了一个新的项目。 这是个信息安全方向的一个项目&#xff0c;之前倒是没有看过。 所以项目的介绍本身是看不出它的重复度的。 但是一往下看 12345的要点&#xff0c;就发现这又是一个烂大街的。 项目本身的逻辑是没有写的。 然后又是所…

【Linux脚本篇】shell变量的使用

目录 &#x1f341;shell变量替换 &#x1f341;定义变量 &#x1f341;shell变量运算 &#x1f342;整数运算 &#x1f342;小数运算 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f990;专栏地址&#xff1a;Linux从入门到精通 shell变量替换 ${变量#匹配规则}…

设计模式 -- 访问者模式

前言 月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂) 央是一片海洋,海乃百川,代表着一块海绵(吸纳万物) 泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出) 月央泽,学习的一种过程,从白纸->吸收各种知识->不断输入输出变成自己的内容 希望大家一起坚持这个过程,也同…

SpringCloud入门实战(七)-Hystrix服务降级

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术&#xff0c;都可以先去官网先看看&…

三翼鸟:传统品牌只盯局部,智慧品牌谋划全局

“当今企业之间的竞争&#xff0c;不是产品之间的竞争&#xff0c;而是商业模式之间的竞争。”很多人都听过现代管理学之父德鲁克的这句话&#xff0c;但又有多少人真正理解了它&#xff1f; 以当下的语境去看&#xff0c;这里其实就是“自利”和“共荣”的区别。前者&#xf…

多臂老虎机问题

1.问题简介 多臂老虎机问题可以被看作简化版的强化学习问题&#xff0c;算是最简单的“和环境交互中的学习”的一种形式&#xff0c;不存在状态信息&#xff0c;只有动作和奖励。多臂老虎机中的探索与利用&#xff08;exploration vs. exploitation&#xff09;问题一直以来都…

Zabbix“专家坐诊”第189期问答汇总

问题一 Q&#xff1a;您好&#xff0c;为什么在shell脚本中&#xff0c;不写mysql命令的绝对路径&#xff0c;zabbix获取不到输出的值&#xff1f; A&#xff1a;mysql默认命令是针对root等有权限才能直接使用的&#xff0c;其他用户要使用要指定命令路径。 Q&#xff1a;zab…

SLB负载均衡haproxy的安装及使用

1.介绍 HAProxy是什么 HAProxy是一个免费的负载均衡软件&#xff0c;可以运行于大部分主流的Linux操作系统上。 HAProxy提供了L4(TCP)和L7(HTTP)两种负载均衡能力&#xff0c;具备丰富的功能。HAProxy的社区非常活跃&#xff0c;版本更新快速&#xff08;最新稳定版1.7.2于2…

【剧前爆米花--爪哇岛寻宝】网络互连,网络通信和网络分层

作者&#xff1a;困了电视剧 专栏&#xff1a;《JavaEE初阶》 文章分布&#xff1a;这是一篇关于网络初识的文章&#xff0c;在这篇文章中讲解了局域网广域网&#xff0c;IP地址&#xff0c;端口以及网络分层等相关内容&#xff0c;希望对你有所帮助&#xff01; 目录 网络互连…

无线通信网 - 无线局域网 WLAN(802.11 标准)

文章目录 1 概述2 WLAN2.1 802.11 标准2.2 网络分类2.3 通信技术 3 扩展3.1 移动通信3.2 网工软考真题 1 概述 #mermaid-svg-UcgosJsdJfSTEm0Y {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-UcgosJsdJfSTEm0Y .err…

PMO和PM必备六大复盘方法工具汇总

无论是对于企业还是个人来说&#xff0c;复盘都是一个能让我们快速成长的方法&#xff0c;尤其是项目经理和PMO&#xff0c;你是带领项目团队的&#xff0c;每一次项目的完成&#xff0c;都有很多经验&#xff0c;俗话说&#xff0c;最大的浪费是经验的浪费&#xff01; 复盘的…

如何解决国外主机托管中遇到的常见问题?

在国际化发展的今天&#xff0c;越来越多的企业和个人选择将网站托管在国外主机上。这样做的好处是显而易见的&#xff0c;如更好的网站访问速度、更多的服务器资源、更优质的服务和更灵活的管理权限等。但同时&#xff0c;使用国外主机也会带来一些问题。本文将讨论国外主机托…

浪潮信息龙蜥联合实验室领衔成立 Serverless SIG 打造标准化开源解决方案

近日&#xff0c;浪潮信息龙蜥联合实验室在龙蜥社区领衔成立 Serverless SIG&#xff08;服务器无感知计算 SIG&#xff09;&#xff0c;并举行了首届 Serverless SIG MeetUp&#xff0c;活动由浪潮信息龙蜥联合实验室主办&#xff0c;来自浪潮信息、天津大学、阿里云、Intel、…

面板数据熵权topsis法分析流程

面板数据熵权topsis法分析流程 一、案例背景 当前有9家公司连续5年&#xff08;2018-2022年&#xff09;的财务指标数据&#xff0c;想要通过这份数据&#xff0c;确定9家公司的财务排名情况。因为各项财务指标的权重有所不同&#xff0c;所以选择使用熵权topsis法进行研究。 …

unity GI 系统

间接光没有办法实现实时计算&#xff0c;所以需要一套GI系统去处理间接光。 GI系统主要解决的是间接光漫反射的实现&#xff0c;实现的载体是LightMap、Light Probe、Refletion Probe。需要一个后台程序&#xff08;离线渲染器&#xff09;来实现离线渲染。可以根据不同的物体…

iPhone清理工具:4Easysoft iPhone Cleaner for Mac

4Easysoft iPhone Cleaner for Mac是一款Mac上的iPhone清理软件&#xff0c;它可以帮助用户清理iPhone上的垃圾文件、缓存文件、无用图片和视频等&#xff0c;从而释放iPhone的存储空间&#xff0c;提高设备的性能。全面扫描您的 iOS 设备并对不必要的数据进行分类。轻松删除 i…

【软件测试面试】面试技巧,让面试官记住的自我介绍,疯狂收割offer.....

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 在讨论如何自我介…

CASAIM全自动3d测量仪自动检测差速器差壳全尺寸测量装配检测

随着汽车行业的新变化&#xff0c;汽车零部件行业也呈现出新的发展趋势。汽车零部件产品作为汽车制造业的配套产业&#xff0c;发展也十分迅速。  差速器作为汽车关键零部件&#xff0c;由差速器差壳、行星齿轮、半轴齿轮、半轴和行星齿轮轴组装而成。 差速器差壳通常采用一…

算法基础(三)(共有20道例题)

七、数学知识 &#xff08;一&#xff09;质数 质数&#xff08;素数&#xff09; 的定义&#xff1a; 互质的定义&#xff1a;除了1以外&#xff0c;两个没有其他共同质因子的正整数称为互质&#xff0c;比如3和7互质。因为1没有质因子&#xff0c;1与任何正整数&#xff08…