二叉树OJ

文章目录

  • 二叉树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;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

哪些蓝牙耳机戴久不疼?长时间佩戴不疼的蓝牙耳机推荐

现在的真无线耳机已经成为了人们的标配之一了&#xff0c;各个厂家也紧随其后&#xff0c;生产出了多种无线耳机&#xff0c;不少人的选购蓝牙耳机一般都是对音质、佩戴和连接&#xff0c;但通常人们佩戴蓝牙耳机都是在半天左右&#xff0c;小编专门整理了一期舒适度高的耳机&a…

ElasticSeach 集成 springboot

声明是ElasticSearch? ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c; 基于RESTful web接口。Elasticsearch是用Java开发的&#xff0c;并作为Apache许可条款下的开放源码发布&#xff0c;是 当前流行的企业级搜索引擎…

Unity 坐标系

一、左手坐标系与右手坐标系 常见的三维软件都采用笛卡尔坐标系&#xff0c;也就是常见的xyz轴坐标系。笛卡尔坐标系可以是左手坐标系也可以是右手坐标系&#xff0c;如下图所示 两种坐标系是镜像对称的。而Unity采用左手系&#xff0c;且xyz轴的默认方向与图中的左手系完全一…

便携式车用CAN分析仪

产品简介 USBCAN-C系列便携式车用CAN分析仪&#xff0c;通过USB接口快速扩展一路CAN通道&#xff0c;使接入CAN网络非常容易&#xff0c;它具有一体式和小巧紧凑的外形&#xff0c;特别适合于随身携带。CAN接口采用金升阳电源模块和信号隔离芯片实现2500V DC电气隔离&#xff0…

KVM存储池管理与磁盘格式

KVM存储池管理与磁盘格式 KVM必须配置一个目录作为存储磁盘镜像&#xff08;存储卷&#xff09;的目录&#xff0c;我们称这个目录为存储池 存储池管理 默认存储池 /var/lib/libvirt/images/ 创建基于文件夹的存储池&#xff08;目录&#xff09; mkdir -p /data/vmfs定义…

WhatsApp如何让客户参与变得更简单?

WhatsApp对你的品牌来说可能和Twitter和Facebook一样重要&#xff0c;你可能已经把它们纳入你的社交媒体战略。 是的&#xff0c;WhatsApp不仅仅可以用来给同事发短信或与远方的亲戚视频聊天&#xff0c;它也适用于商业。 在发展WhatsApp业务时&#xff0c;小企业主得到了最优…

【网络】Socket编程-UDP篇

文章目录 预备知识源IP地址和目的IP地址源MAC地址和目的MAC地址源端口号和目的端口号"端口号port" 和 "进程ID"认识TCP/UDP协议网络字节序 Socket编程sockaddr结构API接口 简单的UDP网络程序服务器server服务端创建套接字:socket函数**socket的底层原理** …

Unity学习笔记 关于Unity相机的FOV以及水平FOV和垂直FOV之间的转换

前言 关于FOV FOV 是在任何给定时间通过人眼、相机取景器或在显示屏上可见的可观察世界的范围。它指的是整个区域的覆盖范围&#xff0c;而不是单个固定焦点。FOV 还描述了一个人可以看到可见世界的角度。 FOV 越宽&#xff0c;可以看到的可观察世界就越多。它是水平、垂直和对…

使用 OpenCV 进行基于 ESP32 CAM 的目标检测和识别

概述:基于 ESP32 CAM 的目标检测和识别 本教程介绍了使用OpenCV基于 ESP32 CAM的目标检测和识别主题。OpenCV 是一个开源的图像处理库,不仅在工业界而且在研发领域都得到了非常广泛的应用。 这里对于对象检测,我们使用了cvlib 库。该库使用 COCO 数据集上的预训练 AI 模型…

JSON-框架的具体使用

JSON-框架的具体使用 非 SpringBoot 项目 Jackson Jackson 是另一个流行的JSON序列化和反序列化库&#xff0c;具有以下特点 速度快&#xff1a;Jackson 采用了高效的JSON解析算法和字节码生成技术&#xff0c;使得其序列化和反序列化速度非常快。支持全类型序列化&#xff1…

4年外包终于上岸,我只能说别去....

我大学学的是计算机专业&#xff0c;毕业的时候&#xff0c;对于找工作比较迷茫&#xff0c;也不知道当时怎么想的&#xff0c;一头就扎进了一家外包公司&#xff0c;一干就是4年。现在终于跳槽到了互联网公司了&#xff0c;我想说的是&#xff0c;但凡有点机会&#xff0c;千万…

C语言-【移位操作符详解】

这篇文章主要介绍了C语言中移位操作符&#xff0c;文章中通过详细的代码以及有关计算机中零碎的知识点对移位操作符进行了一个更好的解释&#xff0c;需要的小伙伴们可以一起学习学习吖&#xff5e; 移位操作符:移动的是补码的二进制序列。 在C语言当中&#xff0c;有两种移位…

「OceanBase 4.1 体验」|OCP Express

文章目录 一、简介二、特性介绍2.1 数据库管理2.2 数据库可观测性 一、简介 OCP Express 是一个基于 Web 的 OceanBase 4.x 轻量化管理工具&#xff0c;作为 OceanBase 数据库的工具组件&#xff0c;它集成在 OceanBase 数据库集群中&#xff0c;支持数据库集群关键性能指标查看…

一个月内面了30家公司,薪资从18K变成28K,真行啊····

工作3年&#xff0c;换了好几份工作&#xff08;行业流行性大&#xff09;&#xff0c;每次工作都是裸辞。朋友都觉得不可思议。因为我一直对自己很有信心&#xff0c;而且特别不喜欢请假面试&#xff0c;对自己负责也对公司负责。 但是这次没想到市场环境非常不好&#xff0c;…

一文搞懂Go错误链

0. Go错误处理简要回顾 Go是一种非常强调错误处理的编程语言。在Go中&#xff0c;错误被表示为实现了error接口的类型的值&#xff0c;error接口只有一个方法&#xff1a; type error interface {Error() string } 这个接口的引入使得Go程序可以以一致和符合惯用法的方式进行错…

【SpringCloud01】

SpringCloud01 1.认识微服务1.0.学习目标1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 2.服务拆分和远程调用2.1.服务拆分原则2.2.服务拆分示例2.2.1.导入Sql语句2.2.2.导入demo工程 2.3.实现远程调用案例2.3.1.案例需求&#xff1a;2.3.2.注册RestTemplate2.…

Java学习笔记 --- Stream流

一、体验Stream流【理解】 案例需求 按照下面的要求完成集合的创建和遍历 创建一个集合&#xff0c;存储多个字符串元素 把集合中所有以"张"开头的元素存储到一个新的集合 把"张"开头的集合中的长度为3的元素存储到一个新的集合 遍历上一步得到的集合 …

使用edge浏览器,白嫖ChatGPT的保姆级教程来了

前言 嗨&#xff0c;大家好&#xff0c;我是希留&#xff0c;一个被迫致力于全栈开发的老菜鸟。 人工智能大浪潮已经来临&#xff0c;对于ChatGPT&#xff0c;我觉得任何一个玩互联网的人&#xff0c;都应该重视起来&#xff0c;用起来。但是国内使用需要解决科学上网、注册、…

计算机基础--->数据结构(1)【图的存储和遍历】

文章目录 图图的存储图的搜索&#xff08;无向无权图&#xff09;代码演示 图 图中包含 顶点、边、度&#xff0c;无向图&#xff0c;有向图&#xff0c;无权图&#xff0c;带权图&#xff0c;其中 度表示一个顶点包含多少条边&#xff0c;有出度和入度。 图的存储 邻接矩阵 代…

(CVE-2022-22965)Spring Framework 远程命令执行漏洞(vulfocus复现)

漏洞原理 该漏洞是SpringFramework数据绑定的一个漏洞&#xff0c;如果后台方法中接受的参数为非基础类型&#xff0c;Spring会根据前端传入的请求正文中的参数的key值来查询与其名称所对应的getter和setter方法&#xff0c;攻击者利用这一特性修改了Tomcat的一个用于日志记录…