牛客101:二叉树

目录

一、二叉树前序遍历

二、二叉树中序遍历

三、二叉树后序遍历

四、二叉树层序遍历

五、按之字形顺序打印二叉树

六、二叉树最大深度

七、二叉树中和为某一值的路径(一)

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

九、对称的二叉树

十、合并二叉树

十一、二叉树的镜像

十二、判断是不是二叉搜索树

十三、判断是不是完全二叉树

十四、判断是不是平衡二叉树

十五、二叉搜索树的最近公共祖先

十六、在二叉树中找到两个节点的最近公共祖先

十七、序列化二叉树

十八、重建二叉树

十九、输出二叉树的右视图


二叉树的三种遍历其实都可以借助栈来做,因为递归过程就像是入栈出栈过程

一、二叉树前序遍历

二叉树的前序遍历_牛客题霸_牛客网

cpp 复制代码
    void preorder(vector<int>&res,TreeNode* root)
    {
        if(!root)return;
        res.push_back(root->val);
        preorder(res, root->left);
        preorder(res, root->right);
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;
        preorder(ret,root);
        return ret;
    }

二、二叉树中序遍历

二叉树的中序遍历_牛客题霸_牛客网

cpp 复制代码
    void inorder(vector<int>& ret,TreeNode* root)
    {
        if(!root)return;
        inorder(ret, root->left);
        ret.push_back(root->val);
        inorder(ret, root->right);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        inorder(ret,root);
        return ret;
    }

三、二叉树后序遍历

二叉树的后序遍历_牛客题霸_牛客网

cpp 复制代码
    void postorder(vector<int>& ret,TreeNode* root)
    {
        if(!root)return;
        postorder(ret, root->left);
        postorder(ret, root->right);
        ret.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        postorder(ret,root);
        return ret;
    }

四、二叉树层序遍历

求二叉树的层序遍历_牛客题霸_牛客网

队列+BFS

cpp 复制代码
    vector<vector<int> > levelOrder(TreeNode* root) {
        vector<vector<int>> ret;
        if(!root)return ret;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            vector<int> tmp;
            int sz=q.size();
            while(sz--)
            {
                TreeNode* t=q.front();
                q.pop();
                tmp.push_back(t->val);
                if(t->left)q.push(t->left);
                if(t->right)q.push(t->right);
            }
            ret.push_back(tmp);
        }
        return ret;
    }

五、按之字形顺序打印二叉树

按之字形顺序打印二叉树_牛客题霸_牛客网

在前一题的基础上将偶数行数组逆序即可,添加一个标记位就能实现

cpp 复制代码
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> ret;
        if(!pRoot)return ret;
        queue<TreeNode*> q;
        q.push(pRoot);
        //标记位
        int flag=1;
        while(!q.empty())
        {
            vector<int> tmp;
            int sz=q.size();
            while(sz--)
            {
                TreeNode* t=q.front();
                q.pop();
                tmp.push_back(t->val);
                if(t->left)q.push(t->left);
                if(t->right)q.push(t->right);
            }
            //偶数行逆序即可,别忘了更新flag
            if(flag%2==0)reverse(tmp.begin(),tmp.end());
            ++flag;
            ret.push_back(tmp);
        }
        return ret;
    }

六、二叉树最大深度

二叉树的最大深度_牛客题霸_牛客网

请输入文本

cpp 复制代码
    int maxDepth(TreeNode* root) {
        if(!root)return 0;
        return 1+max(maxDepth(root->left),maxDepth(root->right));
    }

七、二叉树中和为某一值的路径(一)

二叉树中和为某一值的路径(一)_牛客题霸_牛客网

当结点为叶子结点的时候才判断,不是在root为空判断,不然比如根结点1,左节点空,右节点2的情况,sum=1,也会输出true,但这不是合法路径

cpp 复制代码
class Solution {
    int _sum;
public:
    bool dfs(TreeNode* root, int TmpSum)
    {
        if(!root)return false;
        if(!root->left&&!root->right)
        {
            if(TmpSum+root->val==_sum)return true;
            return false;
        }
        
        return dfs(root->left,TmpSum+root->val)||dfs(root->right,TmpSum+root->val);
    }
    bool hasPathSum(TreeNode* root, int sum) {
        _sum=sum;
        return dfs(root,0);
    }
};

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

二叉搜索树与双向链表_牛客题霸_牛客网

突然发现题目给的就是二叉搜索树,那中序遍历把结点存起来就行了

cpp 复制代码
#include <vector>
class Solution {
	vector<TreeNode*> ans;
public:
	//中序遍历将结点存进数组
	void InOrder(TreeNode*root)
	{
		if(!root)return;
		InOrder(root->left);
		ans.push_back(root);
		InOrder(root->right);
	}
    TreeNode* Convert(TreeNode* pRootOfTree) {
        if(!pRootOfTree)return nullptr;
		InOrder(pRootOfTree);
		for(int i=0;i<ans.size()-1;++i)
		{
			ans[i]->right=ans[i+1];
			ans[i+1]->left=ans[i];
		}
		return ans[0];
    }
};

九、对称的二叉树

对称的二叉树_牛客题霸_牛客网

因为我们需要同时访问两颗子树的数据,仅仅使用一个结点是不够的,我们构造另一个函数。

cpp 复制代码
class Solution {
public:
    bool recursion(TreeNode* root1,TreeNode*root2)
    {
        if(!root1&&!root2)return true;
        //注意判断顺序,能运行到判断不等的代码就说明两个结点都存在
        if(!root1||!root2||root1->val!=root2->val)return false;

        return recursion(root1->left,root2->right)&&recursion(root1->right, root2->left);
    }
    bool isSymmetrical(TreeNode* pRoot) {
        return recursion(pRoot, pRoot);
    }
};

十、合并二叉树

合并二叉树_牛客题霸_牛客网

递归做即可

cpp 复制代码
class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if(!t1)return t2;
        if(!t2)return t1;

        TreeNode* head=new TreeNode(t1->val+t2->val);
        head->left=mergeTrees(t1->left,t2->left);
        head->right=mergeTrees(t1->right, t2->right);
        return head;
    }
};

十一、二叉树的镜像

二叉树的镜像_牛客题霸_牛客网

看懂后其实就是每个根节点的左节点变成右节点,右节点变成左节点,左右节点都交换位置,就达成了镜像,做二叉树递归,一定得有从局部到整体的思想。

递归往下,每层递归交换左右子树

cpp 复制代码
class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        if(pRoot==nullptr)return nullptr;

        //递归子树
        TreeNode*left=Mirror(pRoot->left);
        TreeNode*right=Mirror(pRoot->right);

        //交换,局部->整体
        pRoot->left=right;
        pRoot->right=left;
        return pRoot;
    }
};

十二、判断是不是二叉搜索树

判断是不是二叉搜索树_牛客题霸_牛客网

利用二叉搜索树的中序遍历是递增的这一特性判断

cpp 复制代码
#include <climits>
class Solution {
public:
    int prev=INT_MIN;
    bool isValidBST(TreeNode* root) {
        if(!root)return true;

        if(!isValidBST(root->left))return false;
        if(root->val<=prev)return false;
        prev=root->val;
        if(!isValidBST(root->right))return false;
        return true;
    }
};

十三、判断是不是完全二叉树

判断是不是完全二叉树_牛客题霸_牛客网

借助队列实现层序遍历

cpp 复制代码
class Solution {
public:
    bool isCompleteTree(TreeNode* root) {
        if(!root)return true;
        queue<TreeNode*> q;
        q.push(root);
        bool flag=false;
        while(!q.empty())
        {
            int sz=q.size();
            while(sz--)
            {
                TreeNode* t=q.front();
                q.pop();
                //该层遇到空节点
                if(!t)flag=true;
                //不是空,把left和right传进来,不过得提前判断该层前面有没有出现空
                else
                {   
                    if(flag)return false;
                    q.push(t->left);
                    q.push(t->right);
                }
            }
        }
        return true;
    }
};

十四、判断是不是平衡二叉树

判断是不是平衡二叉树_牛客题霸_牛客网

平衡二叉树判断标准:任何左右子树层数相差不超过1

解法一:自顶向下

cpp 复制代码
class Solution {
public:
    //计算树的最大深度
    int MaxDepth(TreeNode* root)
    {
        if(!root)return 0;
        return 1+max(MaxDepth(root->left),MaxDepth(root->right));
    }
    bool IsBalanced_Solution(TreeNode* pRoot) 
    {
        if(!pRoot)return true;
        
        //处理该层
        int LeftDepth=MaxDepth(pRoot->left);
        int RightDepth=MaxDepth(pRoot->right);
        if(LeftDepth-RightDepth>1||LeftDepth-RightDepth<-1)return false;
        
        //同时左右子树也得平衡
        return IsBalanced_Solution(pRoot->left)&&IsBalanced_Solution(pRoot->right);
    }
};

解法二:自底向上

cpp 复制代码
class Solution {
public:
    //计算该子树深度
    //注意depth是引用传参!!!
    bool judge(TreeNode* root, int& depth){
        //空节点深度为0
        if(root == NULL){ 
            depth = 0;
            return true;
        }

        //递归检查左右子树
        int left = 0, right = 0;
        if(judge(root->left, left) == false)  // 检查左子树是否平衡
            return false;                     // 左子树不平衡,整棵树直接完蛋
        if(judge(root->right, right) == false) // 同理检查右子树
            return false;

        //检查左右子树高度差
        if(left - right > 1 || left - right < -1)
            return false;

        //更新当前节点的深度
        depth = max(left,right)+1;

        //运行到这表示平衡
        return true; 
    }
    bool IsBalanced_Solution(TreeNode* pRoot) {
        int depth = 0;
        return judge(pRoot, depth);
    }
};

十五、二叉搜索树的最近公共祖先

二叉搜索树的最近公共祖先_牛客题霸_牛客网

cpp 复制代码
class Solution {
public:
    int lowestCommonAncestor(TreeNode* root, int p, int q) {
        if(!root)return -1;
        int MinNum=min(p,q),MaxNum=max(p,q);
        //=也在要求内
        if(root->val>=MinNum&&root->val<=MaxNum)return root->val;
        else if(root->val>=MaxNum)return lowestCommonAncestor(root->left, p, q);
        else return lowestCommonAncestor(root->right, p, q);
    }
};

十六、在二叉树中找到两个节点的最近公共祖先

在二叉树中找到两个节点的最近公共祖先_牛客题霸_牛客网

这次不再是二叉搜索树了

既然题目肯定是可以找到公共节点的,所以我们自顶向下遍历即可,遇到相等情况直接返回该局部根,否则就去左右找,左边没有那一定在右边,右边反之。如果左右都有,那么该层root恰好就是我们要的节点。

cpp 复制代码
class Solution {
public:
    TreeNode*dfs(TreeNode*root,int o1,int o2)
    {
        if(!root)return nullptr;
        //此时存在一个节点是局部根的情况
        //既然我们是自顶向下找的,那么我们是第一次碰到直接返回
        if(root->val==o1||root->val==o2)return root;
        
        //查看左右子树有没有目标节点
        TreeNode* IfLeftTree=dfs(root->left,o1,o2);
        TreeNode* IfRightTree=dfs(root->right,o1,o2);
        //左子树没有,那肯定在右子树
        if(!IfLeftTree)return IfRightTree;
        //右子树没有,那一定在左子树
        if(!IfRightTree)return IfLeftTree;
        //左右子树都有,那么该root就是目标返回节点
        return root;
    }
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        return dfs(root,o1,o2)->val;
    }
};

十七、序列化二叉树

序列化二叉树_牛客题霸_牛客网

直接看官方题解吧,这里我们序列化都采用前序遍历

cpp 复制代码
#include <cstring>
#include <string>
class Solution {
public:
    void SerializeFunction(TreeNode*root,string &str)
    {
        if(!root)
        {
            str+='#';
            return;
        }
        //!当作分割
        // str+=(root->val+'0')+'!';  +'0'只适用于0~9
        str+=to_string(root->val)+'!';
        //前序遍历
        SerializeFunction(root->left, str);
        SerializeFunction(root->right, str);
    }
    char* Serialize(TreeNode *root) {    
        if(!root)return "#";
        string str;
        SerializeFunction(root, str);

        //动态开辟成char*类型,不能直接return c_str
        //因为c_str返回指向常量字符串的指针,存在string缓冲区函数结束会被销毁
        char* newstr=new char[str.size()+1];
        strcpy(newstr, str.c_str());
        newstr[str.size()]='\0';
        return newstr;
    }
    //传地址,整个函数共享一个str位置
    TreeNode* DeserializeFunction(char**str)
    {
        if(**str=='#')
        {
            (*str)++;
            return nullptr;
        }
        //数字字符串->数字
        int val=0;
        while(**str!='!'&&**str!='\0')
        {
            val=val*10+**str-'0';
            //别忘记更新str位置,关于优先级问题能打括号尽量打括号吧
            (*str)++;
        }
        TreeNode*root=new TreeNode(val);
        //注意判断是否达到末尾,以防越界
        if(**str=='\0')return root;
        else (*str)++;
        //自顶向下构建二叉树
        root->left=DeserializeFunction(str);
        root->right=DeserializeFunction(str);

        return root;
    }
    TreeNode* Deserialize(char *str) {
        if(*str=='#')return nullptr;
        return DeserializeFunction(&str);
    }
};

十八、重建二叉树

重建二叉树_牛客题霸_牛客网

找到目标点不断递归细分pre数组和vin数组(构建左右子树前序中序遍历,依据这个顺序构建左右子树->递归)

cpp 复制代码
#include <vector>
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int>& preOrder, vector<int>& vinOrder) {
        int n=preOrder.size(),m=vinOrder.size();
        if(n==0||m==0)return nullptr;
        TreeNode*root=new TreeNode(preOrder[0]);
        for(int i=0;i<m;++i)
        {
            if(preOrder[0]==vinOrder[i])
            {
                //构建左子树前序遍历
                vector<int> LTreePre(preOrder.begin()+1,preOrder.begin()+1+i);
                //左子树中序遍历
                vector<int> LTreeVin(vinOrder.begin(),vinOrder.begin()+i);
                //构建左子树
                root->left=reConstructBinaryTree(LTreePre,LTreeVin);

                //右子树也是一样
                vector<int> RTreePre(preOrder.begin()+i+1,preOrder.end());
                vector<int> RTreeVin(vinOrder.begin()+i+1,vinOrder.end());
                root->right=reConstructBinaryTree(RTreePre, RTreeVin);

                break;
            }
        }
        return root;
    }
};

十九、输出二叉树的右视图

输出二叉树的右视图_牛客题霸_牛客网

重建二叉树然后输出每层最右边那个

重建二叉树+层序遍历

cpp 复制代码
#include <vector>
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int>& preOrder, vector<int>& vinOrder) {
        int n=preOrder.size(),m=vinOrder.size();
        if(n==0||m==0)return nullptr;
        TreeNode*root=new TreeNode(preOrder[0]);
        for(int i=0;i<m;++i)
        {
            if(preOrder[0]==vinOrder[i])
            {
                //构建左子树前序遍历
                vector<int> LTreePre(preOrder.begin()+1,preOrder.begin()+1+i);
                //左子树中序遍历
                vector<int> LTreeVin(vinOrder.begin(),vinOrder.begin()+i);
                //构建左子树
                root->left=reConstructBinaryTree(LTreePre,LTreeVin);

                //右子树也是一样
                vector<int> RTreePre(preOrder.begin()+i+1,preOrder.end());
                vector<int> RTreeVin(vinOrder.begin()+i+1,vinOrder.end());
                root->right=reConstructBinaryTree(RTreePre, RTreeVin);

                break;
            }
        }
        return root;
    }
    vector<int> solve(vector<int>& preOrder, vector<int>& inOrder) {
        TreeNode* root=reConstructBinaryTree(preOrder,inOrder);
        vector<int> ret;
        if(!root)return ret;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            int sz=q.size();
            while(sz)
            {
                TreeNode* t=q.front();
                q.pop();
                if(sz==1)ret.push_back(t->val);
                if(t->left)q.push(t->left);
                if(t->right)q.push(t->right);
                --sz;
            }
        }
        return ret;
    }
};

此篇完。

相关推荐
数字化脑洞实验室6 小时前
如何理解不同行业AI决策系统的功能差异?
大数据·人工智能·算法
人邮异步社区6 小时前
推荐几本学习计算机语言的书
java·c语言·c++·python·学习·golang
潼心1412o9 小时前
数据结构(长期更新)第5讲:单链表算法题
数据结构
ha20428941949 小时前
Linux操作系统学习之---线程池
linux·c++·学习
小白菜又菜9 小时前
Leetcode 3370. Smallest Number With All Set Bits
算法·leetcode·职场和发展
A-code9 小时前
C/C++ 中 void* 深度解析:从概念到实战
c语言·开发语言·c++·经验分享·嵌入式
星谷罗殇10 小时前
(七)TRPO 算法 & PPO 算法
算法·机器学习
递归不收敛11 小时前
专属虚拟环境:Hugging Face数据集批量下载(无登录+国内加速)完整指南
人工智能·笔记·git·python·学习·pycharm
web前端神器11 小时前
vitest单元测试测试vue中了element项目安装与运行笔记
笔记