LeetCode | 二叉树高频面试算法题汇总【速来】

news2024/11/17 10:53:08

在这里插入图片描述

小伙子,来给我✍棵树

  • 【LeetCode】144.二叉树的前序遍历
    • C++版本
    • C语言版本(递归算法展开图)
  • 【LeetCode】94.二叉树的中序遍历
    • C++版本
    • C语言版本
  • 【LeetCode】145.二叉树的后序遍历
    • C++版本
    • C语言版本
  • 【LeetCode】102.二叉树的层序遍历
    • DSF——深度优先搜索
    • BSF——广度优先搜索
  • 【LeetCode】965.单值二叉树
  • 【LeetCode】100.相同的树【⭐】
  • 【LeetCode】101.对称二叉树
  • 【LeetCode】226.翻转二叉树
    • DFS
    • BFS
  • 【LeetCode】572.另一棵树的子树
  • 【NowCoder】KY11.二叉树遍历
    • 先序序列转二叉树视频教学
  • DFS与BFS万能模板【❗❗❗】

对于前、中、后序遍历,相信是大家学习二叉树首先掌握的内容,对于这里我会采用递归和非递归的写法来展示,这里首先展示递归写法,非递归在后续更新

【LeetCode】144.二叉树的前序遍历

原题传送门


题目描述.
在这里插入图片描述

思路分析.

  • 思路很简单,专门写一个前序遍历的函数,写法也是前序遍历的写法,这里在函数传参的时候加个应用

代码详解.

C++版本

class Solution {
private:
    void preOrder(TreeNode* root, vector<int> &ret)
    {
        if(root == NULL)
            return;
        ret.push_back(root->val);
        preOrder(root->left,ret);
        preOrder(root->right,ret);
    }
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ret;

        preOrder(root,ret);

        return ret;
    }
};

C语言版本(递归算法展开图)

看完C++版本是不是觉得这道题很简单,我再补充一个C语言版本的,我们来玩玩指针✒

//前序遍历
void PreOrder(struct TreeNode* root, int* a, int* pi)
{
    if(!root)   return;
    a[(*pi)++] = root->val;
    PreOrder(root->left,a,pi);
    PreOrder(root->right,a,pi);
}

//求解二叉树大小
int TreeSize(struct TreeNode* root){
    if(!root)   return 0;
    return TreeSize(root->left) + TreeSize(root->right) + 1;
}           
int* preorderTraversal(struct TreeNode* root, int* returnSize){     
                                            //数组大小(输出型参数)
    //1.获取当前二叉树大小
    *returnSize = TreeSize(root);           
    //2.根据二叉树大小动态开辟数组存放遍历序列
    int* a = (int *)malloc(sizeof(int) * (*returnSize));
    //3.封装前序遍历
    int i = 0;
    PreOrder(root,a,&i);

    return a;
}
  • 可以看到,对于C语言版本的代码看起来即比较冗余和复杂了,因为C语言不像C++那样有现成的STL库,因此有些东西就需要自己去造轮子了,所以对于二叉树前中后序的非递归写法我也会使用C++来实现,后续补充。。。
  • 好,我们来看一下本题的C语言写法,和C++给出的主接口函数不一样的地方是在多了一个【returnSize】的指针,而且返回值也是一个指针,哪有同学问这是啥,其实在C语言的很多代码中有个returnSize是很正常的,因为像LeetCode这种接口型的OJ题它后台内部是有代码写好了会调用你写好的这个接口函数,一般来说他会去获取你这个数组的大小,但是呢这里的返回值是【int*】,也就是一个数组,C语言还可以返回结构体,但是比较麻烦,因此这个【returnSize】其实是外界用来获取你函数中算出来的数组大小的,而且他那里传入的是一个地址,所以在函数内部你可以通过【解引用】的方式来修改这个大小
  • 因此首先我们先去写了一个函数获取传进来的根节点所在的这棵树的大小,使用解引用这个指针变量来接受,这样外界就可以轻而易举地获取到你这个大小了。指针接收地址这一块很重要,后面我们还要讲到
*returnSize = TreeSize(root);      
  • 有了这棵树的大小,我们就需要根据这个大小去开辟出存放二叉树遍历序列的数组了,这里当然是使用malloc来开辟,但是对于C++来看,只需要使用vector这个顺序表就可以了,它会自动扩容的。
  • 在开辟出这个数组后,我们就可以去打印出前序遍历的序列了,我这里再说一个错误的示范
void PreOrder(struct TreeNode* root, int* a, int i)
{
    if(!root)   return;
    a[i++] = root->val;
    PreOrder(root->left,a,i);
    PreOrder(root->right,a,i);
}
  • 可以看到上面我对于最后一个参数我使用的是指针,这个是用来遍历这个数组的,用来存放数据,但是对于这样的写法却存在一定的问题,我们首先提交看看

在这里插入图片描述

  • 可以看到,通过是通过了,但是呢却出现了一些小问题,我们通过画算法图来看看

在这里插入图片描述

  • 可以看到,对于这种结构的树,不会有问题,这就是有些案例可以通过的原因
  • 我们再来看看另一种

在这里插入图片描述

  • 可以看到,对于这种结构的树就会出现问题,从算法图可以看出,因为在这个递归的过程中内部形参i的改变不会影响外部的改变,因此在递归完左子树之后递归右子树i还是原来的那个值,就会造成回退,所以这就是上面题目给出的错误案例中为什么会出现这么大一个随机值的原因了
  • 因此想要真正通过形参的改变影响实参,我们就需要用到【指针接收地址】的思想,就想下面这样
void PreOrder(struct TreeNode* root, int* a, int* pi)
{
    if(!root)   return;
    a[(*pi)++] = root->val;
    PreOrder(root->left,a,pi);
    PreOrder(root->right,a,pi);
}
  • 而且在主函数中还是传入i的地址才行
PreOrder(root,a,&i);
  • 这样提交就可以通过全部的测试案例了

在这里插入图片描述

【LeetCode】94.二叉树的中序遍历

原题传送门


题目描述.
在这里插入图片描述

思路分析.

  • 中序和前序也是一样,换一下根结点放入数组的时机即可

代码详解.

C++版本

class Solution {
private:
    void InOrder(TreeNode* root, vector<int>& ret)
    {
        if(root == NULL)
            return;
        InOrder(root->left, ret);
        ret.push_back(root->val);
        InOrder(root->right, ret);
    }
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;

        InOrder(root,ret);

        return ret;
    }
};

C语言版本

  • 一样,给出C语言的代码求解,不做分析,你可理解前序后自己谢谢看
//中序遍历
void InOrder(struct TreeNode* root, int* a, int* pi)
{
    if(!root)   return;

    InOrder(root->left,a,pi);
    a[(*pi)++] = root->val;
    InOrder(root->right,a,pi);
}

//求二叉树大小
int TreeSize(struct TreeNode* root)
{
    if(!root)   return 0;
    return TreeSize(root->left) + TreeSize(root->right) + 1;
}

int* inorderTraversal(struct TreeNode* root, int* returnSize){
    int i = 0;
    //1.首先获取二叉树大小
    *returnSize = TreeSize(root);

    //2.动态开辟存放中序遍历的数组
    int* a = (int *)malloc(sizeof(int) * (*returnSize));

    //3.中序遍历
    InOrder(root,a,&i);

    return a;
}
  • 下面是运行结果

在这里插入图片描述

【LeetCode】145.二叉树的后序遍历

原题传送门


题目描述.
在这里插入图片描述
思路分析.

  • 后序也是一样的套路。在遍历完左右子树后再将根节点加入数组

代码详解.

C++版本

class Solution {
private:
    void PostOrder(TreeNode* root, vector<int>& ret)
    {
        if(root == NULL)
            return;
        PostOrder(root->left, ret);
        PostOrder(root->right, ret);
        ret.push_back(root->val);
    }
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;

        PostOrder(root,ret);

        return ret;
    }
};

C语言版本

  • 下面是C语言的代码
//后序遍历
void PostOrder(struct TreeNode* root, int* a, int* pi)
{
    if(!root)   return;

    PostOrder(root->left,a,pi);
    PostOrder(root->right,a,pi);
    a[(*pi)++] = root->val;
}

 //求二叉树大小
int TreeSize(struct TreeNode* root)
{
    if(!root)   return 0;
    return TreeSize(root->left) + TreeSize(root->right) + 1;
}

int* postorderTraversal(struct TreeNode* root, int* returnSize){
    int i = 0;
    //1.首先获取二叉树大小
    *returnSize = TreeSize(root);

    //2.动态开辟存放中序遍历的数组
    int* a = (int *)malloc(sizeof(int) * (*returnSize));

    //3.中序遍历
    PostOrder(root,a,&i);

    return a;
}
  • 下面是运行结果

在这里插入图片描述

【LeetCode】102.二叉树的层序遍历

原题传送门


题目描述
在这里插入图片描述

思路分析

  • 对于本题我给出两种解法,也是比较经典的两种思路,一个是DFS【深度优先搜索】以及BFS【广度优先搜索】。
  • 对于DFS,需要通过记录遍历的深度,为每一层开辟或新加结点,以达到分割输出每一层结点的效果;
  • 对于BFS,需要通过队列来进行辅助,只需在原有的BFS代码模板基础上,加一个size去获取每一层的结点个数即可,到那一层的时候出队对应个数的也可以达到分割输出每一层结点的效果

代码详解

  • 给出两种解法,分别是DFS和BFS

DSF——深度优先搜索

class Solution {
private:
    void DFS(TreeNode* cur, vector<vector<int>>& ret, int dep)
    {
        if(cur == NULL) return;
        if(ret.size() <= dep)     //过结果集大小 《= 当前层深度,表明第一层访问本层结点
        {
            vector<int> vec;
            vec.push_back(cur->val);        //创建一个小结果集将其放入
            ret.push_back(vec);
        }
        else
        {
            //若是结果集大小 > 当前层深度,表明已经访问过,直接加入大结果集的当前层小结果集中即可
            ret[dep].push_back(cur->val);        
        }

        //递归其左右子树,开始下一层的访问
        DFS(cur->left, ret, dep + 1);
        DFS(cur->right, ret, dep + 1);      //dep + 1表示深度 + 1
    }
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        int dep = 0;
        DFS(root,result,dep);

        return result;
    }
};
  • 稍微讲解一下DFS的代码,可能有点难以理解,首先看到是需要三个参数,一个是当前所访问的结点,一个是大的结果集,一个是递归深度,这个在我下面所画的算法图中都有展现出来,接下去说一下存放结果集的过程,主要就是通过大结果集的size()大小和当前递归深度做一个比较,若是<=则表示是第一个次遍历到这一层,表明还没有为这一层开辟一个存放数据的小结果集,就开辟出来然后存入数据,若是【ret.size() > dep】了,那就表明此次访问的还是这一层,只需要将新结点追加在这一层之后即可
  • 放入当前结点后便递归其左右子树,当然dep深度也要++,通过这样的逻辑,就可以将一棵树中的所有结点放入一个二维的结果集中
  • 以下是我画的算法图解,仅供参考,你可自己去模拟着画画,强调一点,对于递归要多画展开图

在这里插入图片描述

BSF——广度优先搜索

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> qu;            //二叉树类型队列
        vector<vector<int>> result;     //存放遍历结点结果集
        if(root != NULL)       qu.push(root);      //存入根结点
        //开始队列操作
        while(!qu.empty())      //直至队列为空
        {
            int sz = qu.size();     //获取当前队列的大小
            vector<int> vec;        //装载每一层的树层结点
            for(int i = 0;i < sz; ++i)
            {
                TreeNode* node = qu.front();        //获取当前队列的队头元素
                qu.pop();       //出队队头元素
                vec.push_back(node->val);

                //子树的入队
                if(node->left)    qu.push(node->left);    //左孩子入队
                if(node->right)    qu.push(node->right);   //右孩子入队
            }
            result.push_back(vec);      //每一层结点放入结果集
        }
        return result;
    }
};
  • 然后来说说BFS的具体求解过程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 上述就是BFS层序遍历的过程,你可试着自己模拟看看

【LeetCode】965.单值二叉树

原题传送门


题目描述
在这里插入图片描述

思路分析

  • 本题的描述很简单,【单值二叉树】就是树中的每一个值都是相等的
  • 那我们要怎么去完成这道题目呢?很简单,每次比较其根节点和左右还是是相等即可,若是发现不相等,立马返回false,若是当前相等,则递归其左右子树继续比较,全部比较下来都相等后就访问到空了,便return true

代码详解.

  • 下面是代码详解
bool isUnivalTree(struct TreeNode* root){
    if(root == NULL)
        return true;
        /*
          需判断左右孩子是否为空,防止访问空指针
        */
    if(root->left && root->left->val != root->val)
        return false;
    if(root->right && root->right->val != root->val)
        return false;   

    //若是根结点与其左右子树均相同,则继续递归其左右子树进行判断
    return isUnivalTree(root->left) && isUnivalTree(root->right);   
}
  • 这是它的比较形式

在这里插入图片描述

【LeetCode】100.相同的树【⭐】

原题传送门


本题对于后面的两题都很有帮助,因此要重点理解

题目描述
在这里插入图片描述

思路分析

  • 来看看本题的思路,刚才我们在比较一棵树中的结点是否相同,那只需要左右子树进行一个递归即可,但是现在有两棵树了,那该怎么办呢?可以看到,主函数接口中也是给到了两个指向根节点的指针,那其实套路也是一样的,只是需要考虑的情况多了一些
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
  • 首先就要考虑的是两棵树是不是都是空树的情况,若都是空树,则表示它们也是相等的
  • 其次的话就是要考虑当前传入的两棵树的结点一个空,一个非空的情况,这个条件不可以写在第一个,因为这种情况是包含在两个都非空的情况下的,若是将这个条件写在第一行,那么两棵树进来的当前结点一个空、一个非空就会被【return false】,那就不对了
  • 在空和非空判断完了之后就是结点值的判断,这个很直观,不相同返回false即可
  • 若是上面的条件均不满足,则表示两棵树到目前为止还是相同的,因此我们去分别递归他们的左右子树即可

在这里插入图片描述

代码详解

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    //若是两棵均为空树,则表示相同
    if(p == NULL && q == NULL)
        return true;        
    //空与不空的判断要建立在两棵树都非空的情况下
    if(p == NULL || q == NULL)
        return false;
    if(p->val != q->val)        //比较值是否相等
        return false;

    //均判断完成后表示根与左右孩子均一直,递归其左右子树
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);   
}

【LeetCode】101.对称二叉树

原题传送门


题目描述
在这里插入图片描述

思路分析

  • 好,我们来分析一下思路,对于本题的对称二叉树,也叫做【镜像二叉树】,就是你在家里照镜子一下,你动动左手,那镜子中的人动的就是右手;你动动右手,那镜子中的人动的就是左手。那二叉树也是一下,我的左子树对应过来就是你的右子树,你的右子树呢对应过来就是我的左子树。上面这一段论述你不要以为没有用,可以帮助你理解代码

代码详解

//不到万不得已不要去破坏原有的结构
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    //若是两棵均为空树,则表示相同
    if(p == NULL && q == NULL)
        return true;        
    //空与不空的判断要建立在两棵树都非空的情况下
    if(p == NULL || q == NULL)
        return false;
    if(p->val != q->val)        //比较值是否相等
        return false;

    //均判断完成后表示根与左右孩子均一致,递归其左右子树
    return isSameTree(p->left,q->right) && isSameTree(p->right,q->left);   
}

bool isSymmetric(struct TreeNode* root){
    if(root == NULL)
        return true;
    
    return isSameTree(root->left,root->right);
}
  • 可以看到,我复用了上面那题【相同的树】代码,但是仔细看呢,却发现我修改了一些 ,也及时两棵树的左右子树在递归的时候要往不同的子树走,我走左,你走右;我走右,你就走左
  • 下面是图示

在这里插入图片描述

【LeetCode】226.翻转二叉树

原题传送门


因为刚才讲到了镜像二叉树,和本次的翻转二叉树很像,因此将本题也纳入讲解

题目描述

在这里插入图片描述

思路分析

  • 本题我依旧是给出两个思路,一个是DFS,一个是BFS。
  • DFS很简单,很好理解,因为是翻转二叉树,只需要翻转其左右孩子即可,然后接下向下递归,进行翻转;
swap(root->left,root->right);
invertTree(root->left);
invertTree(root->right);
  • BFS的话和我们上面层序遍历的代码基本一致,只需要在获取到当前结点后交换其左右孩子然后入队即可,后面出队就是翻转后的二叉树
swap(node->left,node->right);

代码详解

DFS

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == NULL)    return root;
        swap(root->left,root->right);
        invertTree(root->left);
        invertTree(root->right);

        return root;
    }
};
  • 以下是DFS的翻转过程

在这里插入图片描述

BFS

  • BFS不做过多讲解吗,核心就在于取到当前队头结点后不要直接将其左右孩子入队,将他们交换一下再入队即可
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        queue<TreeNode*> qu;
        if(root == NULL)    return NULL;
        qu.push(root);

        while(!qu.empty())
        {
            int sz = qu.size();
            for(int i = 0;i < sz; ++i)
            {
                TreeNode* node = qu.front();
                qu.pop();
                //此时node已经取到队头结点
                swap(node->left,node->right);
                //继续遍历左右孩子
                if(node->left)      qu.push(node->left);
                if(node->right)     qu.push(node->right);
            }
        }
        return root;
    }
};

【LeetCode】572.另一棵树的子树

原题传送门


上一题是一段小插曲,本题也需要使用到No.100的代码,也就是【相同的树】,不过本题稍微难理解一些,所以我会讲得详细一点

题目描述

  • 首先来看一下有关题目的描述

在这里插入图片描述

思路分析

  • 本题的意思就是在一棵树中找另一棵树的子树,那有同学说,这该如何去比较呢?
  • 在上面我们学会了如何去比较一棵树中有无相同的结点、两棵树是否相同以及如何去反转一棵树,但是现在的需求是要在一棵树中找到是否包含了另一棵树,存在一个嵌套的逻辑,是会感觉有点烧脑起来了🌊
  • 于是有的同学就找了各种办法,去左右子树里面递归寻找,一个个翻转控制对比,然后把自己绕晕了,其实这道题只要思路对了,是很好解决的,虽然我们是要找是否存在另一颗子树,但是呢这个过程还是在比较两棵树是否是相同的,因此还是需要用到我们上面写到过的【比较两棵树是否相同】的代码逻辑,就是在主接口中在做一些初始判断即可
  • 流程如下
  • 判断主树是否为空,若为空则不可能包含子树,因为题目条件给出子树不为空
  • 判断两棵树的根是否一致,若是一致表示这是两棵相同的树
  • 分别递归其左右子树进行查找对比,复用isSameTree()的代码逻辑

代码详解

//全比一下
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    //若是两棵均为空树,则表示相同
    if(p == NULL && q == NULL)
        return true;        
    //空与不空的判断要建立在两棵树都非空的情况下
    if(p == NULL || q == NULL)
        return false;
    if(p->val != q->val)        //比较值是否相等
        return false;

    //均判断完成后表示根与左右孩子均一致,递归其左右子树
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);   
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    //提示中给出子树不可能为空,因此root为空就表示不存在子树
    if(root == NULL)    
        return false;
    //首先比较两棵树的根是否相同
    if(isSameTree(root,subRoot))
        return true;
    //根不同则去递归root的左右子树继续比较
    return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot); 
                                    //使用逻辑或,左子树找到了就无需在往右子树寻找
}
  • 这里要说明的一点就是在递归的时候为什么用的是【| |】,而不是【&&】,这就需要你自己思考了,这个符号叫做按位或,设想你在左子树中已经找到了包含的子树,那还需要到右子树中去寻找吗?当然是不需要了,按位或的逻辑就是有一个真那整个表达式即为真

递归算法图分解

  • 本题还有一些坑,而且代码可能晦涩难懂一些,因为我画一下递归展开图给你看看,你也可以尝试自己画画
  • 首先是对于正常的情况

在这里插入图片描述

  • 然后是需要注意的情况,可以看到在两棵树中只要哪棵树哪里多出来一个结点,那这两棵树就是不同的,对于DFS来说它会一直递归,直至遍历完所有的树为止
  • 还是一样的缩略图,截不了清晰的图片🐶

在这里插入图片描述

【NowCoder】KY11.二叉树遍历

原题传送门


本题是从【牛客】摘录过来的,也是比较经典的一道题目
对于牛客来说,和力扣不一样的地方在于它是IO型OJ,而力扣则是接口型OJ,对于IO型的OJ呢我们需要和在VS里面一样,头文件、main函数均需要自己添加,也就是输入输出都由自己来完成;但是对于接口型OJ你只需实现题目给出的这一部分功能即可,但是相对许多公司的面试来说,大多都会采用【牛客】这个平台来进行面试,所以大家在熟悉了力扣之后,对于牛客也要多熟悉熟悉

题目描述
在这里插入图片描述

思路分析

  • 本题的意思是这样的,你需要输入一些先序遍历后的二叉树序列,字母表示结点值,#表示空树,然后你要根据这个先序序列去构造一棵二叉树,然后再输出其中序遍历后的结果

先序序列转二叉树视频教学

  • 但是要怎么去将给出的这一个先序序列构造成一棵二叉树呢,因为这不好用文字讲解,因此我将给出视频做讲解📺

先序遍历构造树


——【微信手机端看不到】


看了视频讲解之后,相信你对先序遍历如何去进行构造应该有了一定的了解,接下去我们将这个思路转化为代码的形式

  • 首先第一步,我们要将这个重构的过程封装成一个函数,因为我们肯定要去进行递归,先给出需要传入的参数,说明一下这里为什么要一个指针来接受,而且在主函数中我传入了一个变量的地址,这个变量是用来遍历这个二叉树的先序序列的,这和我们上面在说C语言版本的先序遍历是一个道理,若是在递归的过程中使用的仅仅是普通的变量,虽然递归进去这个i是发生了变化,但是呢在递归回调的时候,却又回到了首次递归进去的那个值,因此在递归右子树时就会出问题,所以这里我们要使用【指针来接受这个变量的地址】,这样每次递归进去内部的修改就可以带动外部的这个值的修改了
BTNode* reBuildTree(char* str, int *pi)
  • 一开始首先就是要判断一下传进来的字符是否为【#】,若是则表示其为一棵空树,我们单独做处理,返回NULL即可,但是不要忘了将这个遍历先序序列数组的指针后移,便于下一次的访问
  • 接下去就是正常的情况,因为我们需要构建出一棵树,树的每一个结点值是需要一棵空间才存放的,而不仅仅是一个char值罢了,因此我们在上面也需要封装一个树结点的结构体,在遇到字母的时候就开辟出一块空间去存放这个结点值,然后继续去递归其左右子树即可,直到碰到【#】就会return递归右子树了
    //若有值,则创建结点存放
    BTNode* node = (BTNode *)malloc(sizeof(BTNode));
    node->data = str[(*pi)++];      //赋值后指向字符序列下一个字符
    
    //递归当前结点的左右子树
    node->lchild = reBuildTree(str,pi);
    node->rchild = reBuildTree(str,pi);
  • 然后有关中序序列的遍历很简单,就是普通的递归,便不做说明

代码详解

  • 下面是整体的代码展示
#include <stdio.h>
#include <stdlib.h>

typedef struct BinaryTreeNode{
    char data;
    struct BinaryTreeNode* lchild;
    struct BinaryTreeNode* rchild;
}BTNode;

                                //指针接收地址
BTNode* reBuildTree(char* str, int *pi)
{
    if(str[*pi] == '#')
    {           //#表示空树
        (*pi)++;
        return NULL;
    }
    
    //若有值,则创建结点存放
    BTNode* node = (BTNode *)malloc(sizeof(BTNode));
    node->data = str[(*pi)++];      //赋值后指向字符序列下一个字符
    
    //递归当前结点的左右子树
    node->lchild = reBuildTree(str,pi);
    node->rchild = reBuildTree(str,pi);

    return node;
}

void InOrder(BTNode* root)
{
    if(root == NULL)
        return;
    InOrder(root->lchild);
    printf("%c ",root->data);
    InOrder(root->rchild);
}

int main() {
    int i = 0;
    char str[100];
    scanf("%s",str);

    BTNode* reTree = reBuildTree(str,&i);
    InOrder(reTree);

    return 0;
}

持续更新中,不断汇总二叉树面试题📰📰📰📰📰


DFS与BFS万能模板【❗❗❗】

  • 可以看到,在二叉树面试题的求解过程中,我大量使用到了两种搜索算法【DFS】和【BFS】,这也是两个非常常用的算法,因此我将给出它们的万能模板,之后遇到题目只需要根据题意进行一些改动即可

DFS

class Solution {
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int depth)
    {
        if (cur == nullptr) return;
        if (result.size() == depth) result.push_back(vector<int>());
        result[depth].push_back(cur->val);
        order(cur->left, result, depth + 1);
        order(cur->right, result, depth + 1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        int depth = 0;
        order(root, result, depth);
        return result;
    }
};

BFS

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        vector<vector<int>> result;
        while (!que.empty()) {
            int size = que.size();
            vector<int> vec;
            // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            result.push_back(vec);
        }
        return result;
    }
};

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

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

相关文章

Linux 之centos7:一、Linux安装

Linux 之centos7 1.Linux简介 ​ Linux内核最初只是由芬兰人李纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而编写的。 ​ Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和UNIX的多用户、多任务、支持多…

C#,基于视频的目标识别算法(Moving Object Detection)的原理、挑战及其应用

本文概述了基于监控视频之连续帧信息的各种目标识别算法及其存在的问题与挑战&#xff0c;结合实际应用开发的工作&#xff0c;文中给出了实验性基于帧差算法和改进型背景算法的非人工智能目标识别算法的实际效果。 目标识别算法一直并将持续成为人工智能研究与应用的重点&…

交换机设备上的G口、F口、E口、S口区别是什么?一台交换机有哪些接口呢?每个接口都有哪些作用?

交换机设备上的G口、F口、E口、S口区别是什么? 一台交换机有哪些接口呢?每个接口都有哪些作用? 交换机的主要功能包括:学习功能、转发过滤和消除回路。 学习功能:以太网交换机知道连接到每个端口的设备的MAC地址,将该地址与相应的端口进行映射,并存储在交换机缓存的MA…

jsp+ssm计算机毕业设计大学生互助系统【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

点云 3D 目标检测 - VoxelNet(CVPR 2018)

点云 3D 目标检测 - VoxelNet&#xff08;CVPR 2018&#xff09;摘要1. 引言1.1 相关工作1.2 贡献2. VoxelNet2.1 VoxelNet架构2.1.1 特征学习网络2.1.2 卷积中层2.1.3 区域提案网络2.2 损失函数2.3 高效实施3. 训练详情3.1 网络详细信息3.2 数据增强4. 实验4.1 KITTI验证集评估…

【算法】动态规划 ⑧ ( 动态规划特点 )

文章目录一、动态规划特点1、求解类型2、方向性3、动态规划状态选择4、动态规划方程设计一、动态规划特点 1、求解类型 求解类型 : 动态规划 必须是求 最值 , 可行性 , 方案数 , 三者之一 , 如果求其它内容 , 则不能使用动态规划算法 ; 求最值 : 最大值 , 最小值 等 ; 大规模问…

[附源码]Node.js计算机毕业设计工资管理系统PPTExpress

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

加入CSDN的第一百天,也是学C的第一百天

加入CSDN的一百天 1.学习总结 2.个人感悟 1.学习总结 学习c语言已经有100天&#xff0c;从一个初出茅庐的无知青年&#xff0c;敲出第一个hello world 都激动的不行&#xff0c;到现在&#xff1a; 常见的数据类型&#xff0c; 变量的命名方式&#xff0c; 变量的分类 到变…

[2022-12-17]神经网络与深度学习 hw9 - bptt

contentshw9 - Back Propagation Through Timetask1题目内容题目思路题目解答题目总结task2题目内容题目思路题目解答题目总结hw9 - Back Propagation Through Time task1 题目内容 推导RNN反向传播算法BPTT。 题目思路题目解答 首先我们要清楚RNN进行前向传播的过程&…

0. Canal 的安装和使用

我看过一场风景&#xff0c;后来我才知道&#xff0c;那是我人生中最美的一段时光。 我爱的人&#xff0c;爱我的人&#xff0c;都能度过这场新型感冒&#xff0c;那该多好。 Canal 的官网: https://github.com/alibaba/canal Canal 能干什么 为什么出现 Canal Canal 是阿里…

[ 数据结构 -- 手撕排序算法第二篇 ] 冒泡排序

文章目录前言一、常见的排序算法二、冒泡排序的实现2.1 基本思想2.2 单趟冒泡排序2.2.1 思路分析2.2.2 单趟代码实现三、冒泡排序的实现五、冒泡排序的时间复杂度5.1 最坏情况5.2 最好情况优化六、冒泡排序的特性总结总结前言 手撕排序算法第一篇&#xff1a;插入排序&#xf…

截止12.17 bitahub踩坑,mask无数次更改,lama代码的那些痛,羊了个羊,imwrite不生效

前面那篇跑出了STCN&#xff0c;倒是STCN熟悉了很多了 对bitahub&#xff0c;需要注意一个问题 要进ssh请用debug卡&#xff01;&#xff01;&#xff01;&#xff01; 要进ssh请用debug卡&#xff01;&#xff01;&#xff01;&#xff01; 要进ssh请用debug卡&#xff01;&…

AQS-semaphoreCyclicBarrierCountDownLatch源码学习

上文&#xff1a;jdk-BlockingQueue源码学习源码下载&#xff1a;https://gitee.com/hong99/jdk8semaphore&cyclicbarrier&CountDownLatch的介绍semaphore基础功能semaphore简称信号量&#xff0c;主要用于控制访问特定资源的线程数目&#xff0c;底层用的是AQS的状记s…

[附源码]Python计算机毕业设计Django万佳商城管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Volatile和高速缓存的关系

“volatile关键字有什么用&#xff1f;” 1 常见理解错误 把volatile当成一种锁机制&#xff0c;认为给变量加上了volatile&#xff0c;就好像是给函数加sychronized&#xff0c;不同的线程对于特定变量的访问会去加锁把volatile当成一种原子化的操作机制&#xff0c;认为加了…

Dubbo 3 Dubbo 快速入门 3.1 Zookeeper 安装

Dubbo 【黑马程序员Dubbo快速入门&#xff0c;Java分布式框架dubbo教程】 3 Dubbo 快速入门 文章目录Dubbo3 Dubbo 快速入门3.1 Zookeeper 安装3.1.1 Zookeeper 安装3.1 Zookeeper 安装 3.1.1 Zookeeper 安装 在Dubbo 架构图中 Dubbo官方推荐使用Zookeeper作为注册中心【Re…

【学习总结】注解和元注解

目录 一、注解 1、注解与XML区别 2、注解的用途 3、注解的三种分类 二、什么是元注解&#xff1f; 1、元注解有几种&#xff1f; 1、Retention存活时间 2、Target使用范围 3、Document保存到javadoc 4、Inherited注解继承 三、如何实现的注解 四、问提&#xff1a; …

为解决BERT模型对语料中低频词的不敏感性

来源&#xff1a;投稿 作者&#xff1a;COLDR 编辑&#xff1a;学姐 &#xff08;内容如有错漏&#xff0c;可在评论区指出&#xff09; 摘要 Dict-BERT为了解决BERT模型对语料中低频词&#xff08;rare words&#xff09;的不敏感性&#xff0c;通过在预训练中加入低频词词典…

人工智能/计算机期刊会议测评(持续更新...更新速度取决于我水论文的速度...)

IEEE Transactions on Knowledge and Data Engineering 中科院2区&#xff0c;CCF A。你为什么是二区&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;做梦都想中的刊。 …

5天带你读完《Effective Java》(二)

《Effective Java》是Java开发领域无可争议的经典之作&#xff0c;连Java之父James Gosling都说&#xff1a; “如果说我需要一本Java编程的书&#xff0c;那就是它了”。它为Java程序员提供了90个富有价值的编程准则&#xff0c;适合对Java开发有一定经验想要继续深入的程序员…