二叉树OJ

news2024/12/24 2:08:59

文章目录

  • 二叉树OJ
    • 根据二叉树创建字符串
      • 思路
      • 示例代码
    • 二叉树的层序遍历
      • 思路
      • 示例代码
    • 二叉树的层序遍历 II
      • 思路
      • 示例代码
    • 二叉树的最近公共祖先
      • 思路1
      • 示例代码1
      • 思路2
      • 示例代码2
    • 二叉搜索树与双向链表
      • 思路1
      • 示例代码1
      • 思路2
      • 示例代码2
    • 迭代实现二叉树的三种遍历
      • 前序遍历
        • 思路
        • 示例代码
      • 中序遍历
        • 思路
        • 示例代码
      • 后序遍历
        • 思路1
        • 示例代码1
        • 思路2
        • 示例代码2

二叉树OJ

根据二叉树创建字符串

606. 根据二叉树创建字符串 - 力扣(LeetCode)

在这里插入图片描述

思路

观察题目, 得出规律:

  1. 节点左右都为空时, 两个括号均省略
  2. 节点左为空时, 不省略左括号
  3. 节点右为空时, 省略右括号

我们创建一个字符串, 先插入根节点(要将数字转为字符串插入) , 然后按照规律递归插入其他节点即可

示例代码

class Solution {
public:
    string tree2str(TreeNode* root) {

        //递归出口: 左右都为空,省略括号,返回空串
        if(root == nullptr)
        return "";

        string s=to_string(root->val);   //先将根节点插入字符串(需要转换成字符串格式)

        //两种情况: 1.左不为空, 不省略括号  2. 左为空, 右不为空, 不省略括号
        if(root->left || root->right)
        {
            s+='(';
            s+= tree2str(root->left);
            s+=')';
        }

        //右为不空, 不省略括号
        if(root->right)
        {
            s+='(';
            s+= tree2str(root->right);
            s+=')';
        }
        return s;
    }
};

二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

在这里插入图片描述

思路

定义一个保存节点指针的队列和一个记录当前层节点数量的变量levelSize;

利用队列进行层序遍历:

  1. 先将根节点入队列, levelSize更新为1
  2. 定义一个二维数组来存储整棵树层序遍历的情况, 当队列不为空时,通过levelSize控制一层一层地出
  3. 定义一个一维数组存储当前层的数据, levelSize>0的条件下,出当前队列的头节点后保存到一维数组中,带入下一层(带入当前节点的左右节点)
  4. 继续出当前层的其他节点,当前层节点出完了即levelSize=0时,把这一整层的数据插入到二维数组中
  5. levelSize=0时当前层节点出完了,下一层节点一定全部进队列了 ,更新levelSize,此时levelSize=q.size()[上一层数全部出完, 这一层全部进队列了,这一层节点个数就是队列中元素的个数]
  6. 重复上面的步骤, 出上一层带下一层,直至队列为空,最后返回二维数组即可

在这里插入图片描述

示例代码

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> q; 
        int levelSize=0;

        //根节点入队列
        if(root) 
        {
            q.push(root);
            levelSize=1;
        }

        vector<vector<int>> vv;
        while(!q.empty())
        {
            //通过levelSize控制一层一层地出
            vector<int> v;
            while(levelSize--)   //levelSize减到0当前层就出完了, 当前层出完了, 下一层一定进队列了
            {
                //出上一层
                TreeNode*front=q.front();
                q.pop();
                v.push_back(front->val);    //当前层的每个数据插入到一维数组中

                //带入下一层
                if(front->left)
                {
                    q.push(front->left);
                }

                if(front->right)
                {
                    q.push(front->right);
                }
            }
            vv.push_back(v);     //将这一整层的数据插入到二维数组中

            //更新下一层的数据
            levelSize=q.size();
        }
        return vv;
    }
};

二叉树的层序遍历 II

107. 二叉树的层序遍历 II - 力扣(LeetCode)

在这里插入图片描述

思路

观察发现这题和上一题输出顺序相反,这题是自底向上的层序遍历,上一题是自顶向下的层序遍历;

所以只需按照上一题的步骤先自顶向下的层序遍历,在返回前逆置这个二维数组就是自底向上的层序遍历

示例代码

class Solution {
public:

    vector<vector<int>> levelOrderBottom(TreeNode* root) {

        //先自顶向下层序遍历后逆置即可

        queue<TreeNode*> q; 
        int levelSize=0;

        //根节点入队列
        if(root) 
        {
            q.push(root);
            levelSize=1;
        }

        vector<vector<int>> vv;
        while(!q.empty())
        {
            //通过levelSize控制一层一层地出
            vector<int> v;
            while(levelSize--)   //levelSize减到0当前层就出完了, 当前层出完了, 下一层一定进队列了
            {
                //出上一层
                TreeNode*front=q.front();
                q.pop();
                v.push_back(front->val);    //当前层的每个数据插入到一维数组中

                //带入下一层
                if(front->left)
                {
                    q.push(front->left);
                }

                if(front->right)
                {
                    q.push(front->right);
                }
            }
            vv.push_back(v);     //将这一整层的数据插入到二维数组中

            //更新下一层的数据
            levelSize=q.size();
        }
        reverse(vv.begin(),vv.end());

        return vv;
    }

};

二叉树的最近公共祖先

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

在这里插入图片描述

思路1

观察题目发现特征,分为两种情况:

  1. 两个节点中一个是根, 另一个是孩子, 那么根就是公共祖先
  2. 一个节点在我的左树,一个节点在我的右树,那么我就是公共祖先

所以找公共祖先的步骤:

  1. 满足情况1直接返回
  2. 一般情况下,要判断两个节点在左右哪个子树,写一个isinTree函数来判断
  3. 判断结果分3种: (1) 一个节点在我的左树,一个节点在我的右树,那么我就是公共祖先 (2) 两个节点都在左子树, 转化成子问题, 去左子树找 (3) 两个节点都在右子树, 转化成子问题, 去右子树找

但此思路效率较低,我们用思路2来改进

在这里插入图片描述

示例代码1

class Solution {
public:

    bool isinTree(TreeNode* root,TreeNode* x)
    {
        if(root==nullptr)
            return false;
        
        if(root==x)
            return true;

        return isinTree(root->left,x) || isinTree(root->right,x);
    }

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {

        if(root==nullptr)
            return nullptr;

        //两个节点中有一个是根, 那么这个节点就是公共祖先
        if(p==root || q==root)
            return root;

        //真正找公共祖先的过程

        //判断两个节点在左右哪个子树
        bool pInleft=isinTree(root->left,p);
        bool pInright=!pInleft;

        bool qInleft=isinTree(root->left,q);
        bool qInright=!qInleft;

 
        //1. 一个在我的左树, 一个在我的右树, 我就是公共祖先
        if((pInleft && qInright) || (qInleft && pInright))
        {
            return root;
        }
        else if(pInleft && qInleft)  //2. 都在左子树, 转化成子问题, 去左子树找
        {
            return lowestCommonAncestor(root->left, p, q);
        }
        else    //3. 都在右子树, 转化成子问题, 去右子树找
        {
            return lowestCommonAncestor(root->right, p, q);
        }
    }
};

思路2

前提: 如果是三叉链(每个节点都有parent), 可以转化成链表相交的问题

此解法以这个前提为思路:DFS, 求出这两个节点的路径保存在容器中, 转换成路径相交问题(这里用栈保存)

求两个节点的路径:

  1. 先让节点入栈, 若此节点恰好是直接返回
  2. 递归到左子树去找, 左是直接返回 ; 递归到右子树去找, 右是直接返回
  3. 若左右都不是, 说明当前节点不在目标节点的路径上, 直接让当前节点出栈, 返回false到上一层继续找

转换成路径相交:

  1. 用栈来存储两个节点的路径上的各个节点
  2. 两个栈中元素个数不相等, 先出元素多的栈
  3. 两个栈中元素个数相等, 两个栈中元素都出栈
  4. 直至两个栈中栈顶元素都相等, 则此节点就是公共祖先

在这里插入图片描述

示例代码2

class Solution {
public:

    bool GetPath(TreeNode* root, TreeNode* x,   stack<TreeNode*>& path)
    {
        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();
    }
};

二叉搜索树与双向链表

二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

在这里插入图片描述

思路1

思路1比较简单,首先将这棵二叉树进行中序遍历并将其节点存到数组中,后更改数组中的链接关系,返回数组的首元素即可

示例代码1

class Solution {
public:
	vector<TreeNode*> v;  //用来存放中序遍历的节点

	//中序遍历将结点放入数组中
	void Inorder(TreeNode* cur) 
	{
		if(cur==nullptr)
			return;

		Inorder(cur->left);
		v.push_back(cur);
		Inorder(cur->right);
    }
    TreeNode* Convert(TreeNode* pRootOfTree) {

		if(pRootOfTree==nullptr)
			return pRootOfTree;

		Inorder(pRootOfTree);

		//更改数组中的链接关系
		for(int i=0;i<v.size()-1;++i)
		{
			v[i]->right=v[i+1];
			v[i+1]->left=v[i];
		}
		return v[0];   //返回双向链表的头节点
    }
};

思路2

中序遍历,更改树的链接关系,定义一个prev节点和一个cur节点,让cur节点从根开始走一个中序遍历,让cur -> left = prev, prev->right=cur 递归更改链接关系即可;更改完后找一下这个双向链表的头节点返回头节点

在这里插入图片描述

示例代码2

class Solution {
public:

	void InorderConvert(TreeNode* cur, TreeNode*& prev) 
	{
		if(cur==nullptr)
			return;

		InorderConvert(cur->left, prev);

		//这里cur出现顺序就是中序
		cur->left=prev;
		if(prev)
			prev->right=cur;

		prev=cur;

		InorderConvert(cur->right, prev);
    }

    TreeNode* Convert(TreeNode* pRootOfTree) {

		TreeNode*prev=nullptr;
		InorderConvert(pRootOfTree, prev);

		//找头节点
		TreeNode*head=pRootOfTree;
		while(head && head->left)
		{
			head=head->left;
		}

		return head;   
    }
};

迭代实现二叉树的三种遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

94. 二叉树的中序遍历 - 力扣(LeetCode)

145. 二叉树的后序遍历 - 力扣(LeetCode)

前序遍历

思路

先访问这棵树的左路节点, 将访问到的元素入栈同时插入到数组中,左路节点访问完后, 开始从栈中取左路节点(即栈顶元素), 这时表示左路节点的左子树已访问完了;后开始访问左路节点的右子树,当栈为空和当前节点为空时循环结束

总结: 访问左路节点 + 转化成子问题访问左路节点的右子树

在这里插入图片描述

示例代码

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {

        stack<TreeNode*> st;
        vector<int> v;
        TreeNode*cur=root;

        while(!st.empty() || cur)
        {
            //访问一棵树: 1. 访问左路节点  2. 访问左路节点的右子树

            //访问左路节点
            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) {
        TreeNode*cur=root;
        stack<TreeNode*> st;   //临时存储树中的节点
        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;
    }
};

后序遍历

思路1

思路与上面两种遍历相似

后序遍历要找到根节点可以访问的两种情况: 1. 当其右为空时 2. 当右子树已经访问过时

右为空直接判断;右子树是否已经访过了, 用一个前驱节点prev来记录访问, 直接判断根的右节点是否为前驱节点

在这里插入图片描述

示例代码1

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        TreeNode*cur=root;
        stack<TreeNode*> st;   //临时存储树中的节点
        vector<int> v;         //保存这棵树的遍历结果

        TreeNode*prev=nullptr;
        while(cur || !st.empty())
        {
            //开始访问左路节点
            while(cur)
            {
                st.push(cur);
                cur=cur->left;
            }

            //从栈里面取到左路节点, 表示左路节点的左子树访问完了
            TreeNode*top=st.top();

            //可以访问根节点的两种情况: 1. 右为空  2. 右子树已经访问过了
            if( top->right==nullptr || top->right==prev)
            {
                st.pop();
                v.push_back(top->val);

                prev=top;
            }
            else
            {
                //访问左路节点的右子树
                cur=top->right;
            }
        }
        return v;
    }
};

思路2

还是前序遍历的思路, 只是这次是访问右路节点 + 转化成子问题访问右路节点的左子树

最后将这个数组逆置就由根-右-左 变成左-右-根

示例代码2

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        TreeNode*cur=root;
        stack<TreeNode*> st;   //临时存储树中的节点
        vector<int> v;         //保存这棵树的遍历结果

        while(cur || !st.empty())
        {
            //开始访问右路节点
            while(cur)
            {
                v.push_back(cur->val);
                st.push(cur);
                cur=cur->right;
            }

            //从栈里面取到右路节点, 表示右路节点的右子树访问完了
            TreeNode*top=st.top();
            st.pop();

            cur=top->left;
        }

        //由根-右-左 变成左-右-根
        reverse(v.begin(), v.end());

        return v;
    }
};

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

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

相关文章

从零开始:如何成为一名优秀的品牌策划师

作为一个十年老策划&#xff0c;告诉你我们公司&#xff08;一个比较牛的品牌策划公司&#xff09;当年是怎么培养新人的吧。 1、看书 你必须要看六本书&#xff0c;他们是&#xff1a;特劳特的《定位理论》、《营销4.0》、《品牌王道》、《商战》、《竞争优势》&#xff0c;…

一图看懂 multidict 模块:类似于字典的键值对集合,键可以多次出现,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 multidict 模块&#xff1a;类似于字典的键值对集合&#xff0c;键可以多次出现&#xff0c;资料整理笔记&#xff08;大全&#xff09; &#x1f9ca;摘要&#x1f9ca;模…

数据库sql语句(count(*)和count(字段))

例题&#xff1a; 创建如下两张表 分别命名为books和persons &#xff08;1&#xff09;按照书名&#xff0c;姓名的顺序列出字里包含‘德’字的人物的姓名&#xff0c;书名和字。 select name 姓名,bookname 书名,style 字 from books,persons where style like %德% and bo…

SpringBoot配置文件相关

SpringBoot配置文件内容分为两类: 1.Spring自带的配置,比如server.port(这玩意就自己躺在application.properties里) 2.自定义的配置 配置文件的格式分为两种 1.properties格式 2.yml格式 properties和yml的区别 1.通用性 properties是SpringBoot项目默认的配置文件!他已经很老…

Baumer工业相机堡盟工业相机软件CameraExplorer常见功能使用说明

Baumer工业相机堡盟工业相机软件CameraExplorer常见功能使用说明 Baumer工业相机Baumer工业相机图像采集功能Baumer工业相机图像基本参数设置 Baumer工业相机 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机&#xff0c;可用于各种应用场景&#xff0c;如物体检测、计…

诺亚财富财报不及预期,收入大幅下滑27.8%,股价也已下跌26%

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 诺亚财富2022财年业绩和管理层评论 在此前于2023年3月下旬举行的2022年第四季度财报会议上&#xff0c;诺亚财富(NOAH)强调&#xff0c;“我们希望将2022年的痛苦转化为2023年的收获。”虽然诺亚财富在财报会议上没有提供2…

三子棋的实现【C语言】

完成一个三子棋游戏的实现包括三部分 test.c 测试游戏 game.c 实现游戏 game.h 声明游戏 菜单 首先我们完成游戏的菜单部分 游戏部分 完成三子棋我们需要完成棋盘的创建&#xff0c;玩家下棋&#xff0c;电脑下棋&#xff0c;判断胜负&#xff0c;以及将棋盘展现给玩家&a…

WebApi安全性 使用TOKEN+签名验证

&#xff08;2&#xff09;在请求头中添加timespan&#xff08;时间戳&#xff09;&#xff0c;nonce&#xff08;随机数&#xff09;&#xff0c;staffId&#xff08;用户Id&#xff09;&#xff0c;signature&#xff08;签名参数&#xff09;    //加入头信息request.Hea…

shell中函数的应用(题型列举)

1、编写函数&#xff0c;实现打印绿色OK和红色FAILED 判断是否有参数&#xff0c;存在为Ok&#xff0c;不存在为FAILED 第一步&#xff1a;进入脚本文件进行编辑 第二步&#xff1a;编辑函数脚本文件 colour() {if [ $# -ne 0 ];thenecho -e "\033[32m OK \033[0m"e…

3D樱花照片墙、3D樱花照片墙有文字、红蓝爱心、流星雨3D旋转相册、文字加爱心

前端页面百度云盘自提 3D樱花照片墙 3D樱花照片墙有文字 红蓝爱心 流星雨3D旋转相册 文字加爱心

数据治理之关键环节元数据管理开源项目datahub探索

文章目录 概述定义核心功能概念元数据应用其他开源 架构概览组件元数据摄取架构服务体系结构 本地部署环境要求安装摄取样例 摄取入门介绍核心概念命令行MySQL摄取示例配置ClickHouse摄取示例 概述 定义 datahub 官网地址 https://datahubproject.io/ 最新版本v0.10.2 datahub…

怎么将m4a转换成mp3?这三种方法不妨试试看吧

将M4A转换为MP3具有重要作用。首先&#xff0c;MP3格式是一种通用的音频格式&#xff0c;几乎所有的播放器和设备都支持它。而M4A格式则不如MP3格式广泛。如果我们想在多个设备上播放M4A音频文件&#xff0c;有时候需要将其转换为MP3格式。其次&#xff0c;M4A文件通常比MP3文件…

计算机专业含金量高的证书

目录 第一种证书&#xff1a;计算机技术与软件专业资格考试证书 第二种证书&#xff1a;微软认证 第三种证书&#xff1a;Oracle认证 第四种证书&#xff1a;思科认证 第五种证书&#xff1a;华为认证 第六种证书&#xff1a;红帽认证工程师 第七种证书&#xff1a;阿里…

Python每日一练(20230512) 跳跃游戏 V\VI\VII

目录 1. 跳跃游戏 V 2. 跳跃游戏 VI 3. 跳跃游戏 VII &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 跳跃游戏 V 给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到&a…

蒙层禁止下方页面滚动防抖动完美方案

学习链接 js如何禁止滚动条滚动&#xff0c;但不消失&#xff01; - 这个是完美解决方案&#xff08;在线demo示例&#xff09; 解决窗口滚动条消失而导致的页面内容抖动的问题 完美解决js 禁止滚动条滚动&#xff0c;并且滚动条不消失&#xff0c;页面大小不闪动 蒙层禁止…

【Python数据类型-元组】------- PYTHON基础11

内容目录 一、 元组1. 元组的构建2. 元组的索引3. 元组和列表的区别及相互转换3.1. 列表转为元组&#xff0c;通过内置函数tuple()实现&#xff0c;比如&#xff1a;3.2. 元组转为列表&#xff0c;通过内置函数list()实现 4. 元组的基本操作&#xff1a;更新&#xff0c; 删除&…

用于colmap重建结果、pcd/ply、6D位姿的点云可视化工具

工具介绍&#xff1a;提供一款用于点云可视化windows的工具 可视化的对象包括&#xff1a; 1、colmap重建结果 2、pcd\ply格式的点云 3、位姿R|t可视化 4、在线接收点python发送的坐标 其他功能&#xff1a;点云保存、颜色修改、点云隐藏、点云大小调整 工具地址&#xff1a…

Qt之QGraphicsEffect的简单使用(含源码+注释)

文章目录 一、效果示例图1.效果演示图片3.弹窗演示图片 二.问题描述三、源码CFrame.hCFrame.cppCMainWindow.hCMainWindow.cpp 总结 一、效果示例图 1.效果演示图片 3.弹窗演示图片 二.问题描述 &#xff08;因为全是简单使用&#xff0c;毫无技巧&#xff0c;直接描述问题&a…

计算机视觉的深度学习 Lecture3:Linear Classifiers 笔记 EECS 498.008

注意到每一行完成一类的分类 事先思考一下loss的可能值有助于debug。如果W随机为高斯分布&#xff0c;μ为0.001&#xff0c;那么下面sj-syi就会很小&#xff0c;Li的值接近C-1&#xff0c;C为分类数 正则化表达式&#xff1a; 如果score都是随机很小的数&#xff0c;近似意…

博客管理系统--博客详情页、登录页

登录页实现强制登录 URL解决后&#xff1b;现在到查看全文按钮。我们点击这个查看全文我们就跳转到博客详情页。 我们希望就是在这个页面&#xff1b;把这些写死的数据换成从后端获取的。 1&#xff1a;约定前后端交互接口 请求&#xff1a;GET /blog?blogId1 (这样子写和博…