代码随想录算法训练营第18天|● 513.找树左下角的值● 112. 路径总和 113.路径总和ii● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

513.找树左下角的值

思路(递归):题意是找树的最后一行,最左边的值,所以不一定该节点是左孩子,也有可能是右孩子。本题不需要中的处理过程,所以前中后序遍历都可以,因为只需要左右的遍历顺序即可。用一个全局变量MaxDepth记录最大的深度,depth记录当前遍历的层数(所以需要回溯过程)。因为最先递归遍历左边,所以如果当第一次最大深度出现节点,则result一定记录的是最左值,再之后遍历如果最大深度改变则记录替换之前的值。

代码:

int MaxDepth = INT_MIN;//记录树的最大深度
    int result;//存放最终节点的值
    void traversal(TreeNode* node,int depth){//depth记录当前遍历到的深度
        if(node->left==nullptr && node->right==nullptr){//终止条件
            if(MaxDepth < depth){
                MaxDepth = depth;
                result = node->val;
            }
            return;//如果当前不是最大深度但又是叶节点则会返回上一个节点
        }
        if(node->left){//左
            depth++;
            traversal(node->left,depth);
            depth--;//回溯深度
        }
        if(node->right){//右
            depth++;
            traversal(node->right,depth);
            depth--;
        }
        return;
    }
    int findBottomLeftValue(TreeNode* root) {
        traversal(root,0);
        return result;
    }

思路(迭代法):本题用层序遍历是最简单的方法,只需要每次记录每一行的第一个节点值即可.

代码:

int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=nullptr)       que.push(root);
        int result = 0;
        while(!que.empty()){
            int size = que.size();
            for(int i = 0;i < size;i++){
                TreeNode* node = que.front();//该行最左的第一个节点
                if(i == 0) result = node->val;//i=0,则这一行刚开头,记录下结果
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
                que.pop();
            }
        }
        return result;
    }

112.路径总和

思路(递归法):首先遍历顺序由于没有需要中节点的处理逻辑,所以中为空,遍历顺序左右都可以。

正常思路都是把每遍历的节点进行累加,若等于目标值且到了叶子节点则符合目标路径。这种方法需要count值设为0,但是还需要传目标值,会比较麻烦。下面的方法是count值直接为目标值,每遍历一个节点就减去该节点的目标val值。最后遍历到叶子节点且count为0则为目标路径。

注1:返回true和false的条件多,这题只需要找到其中一条满足条件的路径,所以如果找到目标路径一直往上返回true,所以需要返回值.若只是遍历的题目,不需要返回值。

注2:需要用到回溯,有递归就有回溯,这题和上题的思路相似。

代码:

bool traversal(TreeNode* node,int count){//count是目标值,每遍历一个节点减去节点值,最后为0且叶子节点则是目标路径
        //终止条件1,遍历到叶子节点且count已经减到0,则是目标路径
        if(node->left==nullptr && node->right==nullptr && count==0)
            return true;
        //终止条件2,遍历到叶子节点但count不为0,则不是目标路径
        if(node->left==nullptr && node->right==nullptr && count!=0)
            return false;
        //左右遍历顺序
        if(node->left){//左
            count-=node->left->val;
            if(traversal(node->left,count))//如果返回结果是真,则找到了目标路径,所以不需要进行下一步回溯,直接一层层返回true
                return true;
            count+=node->left->val;//若上面条件不满足,说明到了叶子节点但是不满足条件,所以要回溯
        }
        if(node->right){//右
            count-=node->right->val;
            if(traversal(node->right,count))
                return true;
            count+=node->right->val;//回溯
        }
        return false;//从根出发经历了上面的遍历,都没有返回true说明没有符合条件的路径
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root==nullptr)
            return false;
        return traversal(root,targetSum-root->val);
    }

113.路径总和ii

思路(递归法):这题思路112题和257.二叉树的所有路径结合,找符合目标值的路径112题,把路径加入结果集257题。

代码:

vector<vector<int>> result;//存放结果
    vector<int> path;//存放当前路径
     void traversal(TreeNode* node,int count){
        //终止条件1,符合目标路径,把当前路径放入结果集
        if(node->left==nullptr && node->right==nullptr && count==0){
            result.push_back(path);
            return;
        }
        //终止条件2,遍历到叶节点但不符合目标路径
        if(node->left==nullptr && node->right==nullptr && count!=0)
            return;

        if(node->left){//左
            count-=node->left->val;
            path.push_back(node->left->val);
            traversal(node->left,count);
            count+=node->left->val;//回溯
            path.pop_back();//回溯
        }
        if(node->right){//右
            count-=node->right->val;
            path.push_back(node->right->val);
            traversal(node->right,count);
            count+=node->right->val;//回溯
            path.pop_back();//回溯
        }
        return;
    }
 
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        result.clear();
        path.clear();
        if(root==nullptr)
            return result;
        path.push_back(root->val);
        traversal(root,targetSum-root->val);
        return result;
    }

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

思路:首先要理解构造二叉树的理论知识。整体思路是确定切割点,将其中序数组和后序数组进行切割分为左右子树的集合,再递归切割构造。

以下分步骤解析:

第一步:从中序数组中确定切割点在数组的下标(位置)(后序数组末尾是中间节点也是要在中序数组中找的切割点)

第二步:利用中序切割点的位置把中序数组切割为左中序,右中序数组(不包括切割点)

第三步:把后序数组切割为左后序数组和右后序数组;ps:中序是有已知的切割点当然方便切割,但是切割点也就是中间节点在后序数组是最后一个节点,所以并不能作为后序数组切割的依据。但是从另一个方面来看,第一二步中已经明确划分了左右子树的集合了(左右中序数组)。子树集合大小无论在前中后序中都是不变的,这个可以作为后序数组切割依据。

第四步:递归进行切割构造(分别传入左子树集合包括左中序数组,左后序数组;传入右子树集合包括右中后序数组)

最后在传会其构造节点

注:这题还需要遵守递归的循环不变量原则,即切割的区间。

代码:

TreeNode* traversal(vector<int>& inorder,vector<int>& postorder){//inorder中序数组,postorder后序数组
        //终止条件1,因为主函数有了判断是否为空数组,这个条件是递归循环时遇到传入空数组的终止情况
        if(postorder.size() == 0) return nullptr;
        int rootValue = postorder[postorder.size() - 1];//后序数组的最后一个元素是当前的中间节点
        TreeNode* root = new TreeNode(rootValue);
        //终止条件2,情况1:第一次递归数组只有一个节点,返回当前节点;情况2:递归传入数组只有一个节点,即是叶子节点,所以下面不需要再递归,把当前叶子节点返回
        if(postorder.size() == 1) return root;

        /*第一步.从中序数组确定切割点的位置*/
        int delimiterIndex;
        for(delimiterIndex = 0;delimiterIndex < inorder.size();delimiterIndex++){
            if(inorder[delimiterIndex] == rootValue) break;
        }//遍历结束,delimiterIndex就是中序数组的切割点位置

        /*第二步.切割中序数组使其分为左中序和右中序数组(不包含中节点)*/
        //左区间,当前区间为左闭右开[0,delimiterIndex)
        vector<int> leftInorder(inorder.begin(),inorder.begin() + delimiterIndex);
        //右区间,[delimiter+1,end)
        vector<int> rightInorder(inorder.begin() + delimiterIndex+1,inorder.end());

        /*第三步,切割后序数组使其为左后序和右后序数组,有个注意的点,因为中间节点是在后序数组末尾,所以要舍弃*/
        //左闭右开,切割点无论左右区间的长度都和中序时一样的,所以和中序切割方法不同,用其长度进行切割
        postorder.resize(postorder.size()-1);//舍弃最后一个元素
        //左区间,[0,leftIndex)
        vector<int> leftPostorder(postorder.begin(),postorder.begin() + leftInorder.size());
        //右区间,(leftIndex,end],后序不像中序,后序根节点已经在数组末尾并且被舍弃,所以切割是连续的
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(),postorder.end());

        /*第四步,把左子树集,右子树集进行递归切割*/
        root->left = traversal(leftInorder,leftPostorder);//传入左中序,左后序集合
        root->right = traversal(rightInorder,rightPostorder);//传入右中序,右后序

        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(inorder.size()==0 || postorder.size()==0)    return nullptr;
        return traversal(inorder,postorder);
    }

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

思路:和106思路是一样的,唯一区别是要知道前序的根节点是在数组第一个位置。所以代码在切割部分注意区分即可。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
代码随想录算法训练是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练,讲解了二叉树的理论基础、递归遍、迭代遍和统一遍的内容。此外,在讨论还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍方法。 训练还提供了每日的讨论知识点,例如在第15天的讨论,介绍了层的方法和使用队列来模拟一层一层遍的效果。在第16天的讨论,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值