代码随想Day 18 | 513.找树左下角的值、112. 路径总和 113.路径总和ii、106.从中序与后序遍历序列构造二叉树

这道题目我看到后的第一思路是:层序遍历,每层的第一个都有可能是所需要返回的值,依据层序遍历的模板写代码即可:

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        int res;
        queue<TreeNode*> q;
        if(root==nullptr) return INT_MAX;
        q.push(root);
        while(!q.empty())
        {
            int len=q.size();
            res = q.front()->val;
            for(int i=0;i<len;i++)
            {
                TreeNode* node=q.front();
                q.pop();
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
        }
        return res;

    }
};

当然这道题目也可以用递归法,带有回溯思想,递归三部曲:

1. 确定递归函数的参数和返回值

参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。代码如下:

int maxDepth = INT_MIN;   // 全局变量 记录最大深度
int result;       // 全局变量 最大深度最左节点的数值
void traversal(TreeNode* root, int depth)

2. 结束条件

当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。

3. 在找最大深度的时候,递归的过程中依然要使用回溯。

详细代码如下:

class Solution {
public:
    int res;
    int max_depth = -1;
    void dfs(TreeNode* root, int depth)
    {
        if(root->left==nullptr&&root->right==nullptr)
        {
            if(depth>max_depth)
            {
                max_depth = depth;
                res = root->val;
            }
            return ;
        }
        if(root->left) dfs(root->left, depth+1); //带有回溯
        if(root->right) dfs(root->right, depth+1);
    }
    int findBottomLeftValue(TreeNode* root) {
        //递归法 核心思路是最左边的节点就是第一个深度变大的节点之一
        dfs(root,0);
        return res;

    }
};

 112. 路径总和  

这道题目返回bool值,所以我的思路是使用后序遍历来做,遍历完后返回根节点的结果,递归三步走:

1. 参数: 返回类型就是bool,需要的参数有节点和tatget与各个节点的差,不用再另写函数;

2. 结束条件:如果节点为空,说明没找到符合条件的路径,返回空;如果当前节点不为空,但是当前target==root->val,且当前节点是叶子节点,返回true;否则false;

3. 处理每层逻辑:

后序遍历,然后返回左右子树的逻辑或,这样有一条路径就可返回true

详细代码如下:

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root==nullptr) return false;//结束条件
        if(root->left==nullptr&&root->right==nullptr)
        {
            if(targetSum==root->val) return true;
            return false;
        }
        //每层逻辑 后序
        bool left = hasPathSum(root->left,targetSum-root->val); //左
        bool right = hasPathSum(root->right, targetSum-root->val); //右
        return left||right;  //中
    }
};

总结小tips:

递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:

  • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况如113.路径总和ii)
  • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况在236. 二叉树的最近公共祖先 (opens new window)中介绍)
  • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)

113.路径总和II

这道题目和112十分相似,但是这道题需要返回每个路径,不能找到一条就返回了,要把所有的路径都遍历完,递归三步走:

1. 参数:

不需要返回参数,传入的参数则需要有一个记录当前路径的以及节点,以及一个储存结果的变量:

2. 结束条件:

遇到叶子节点且求和符合条件,就把path加入到结果变量res中;

3. 每层逻辑:

根左右;

我自己写了思路,但是不能AC,错误代码如下:

class Solution {
public:
    vector<vector<int>> res;
    void dfs(TreeNode* root, int targetSum, vector<int>& path)
    {
        if(root==nullptr) return ;
        path.push_back(root->val); //中
        if(root->left==nullptr&&root->right==nullptr)
        {
            if(root->val==targetSum)
            {
                res.push_back(path);
            }
            return ;
        }
        dfs(root->left,targetSum-root->val,path);
        path.pop_back();
        dfs(root->right, targetSum-root->val,path);
        path.pop_back();

    }
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<int> path;
        dfs(root,targetSum,path);
        return res;

    }
};

进行代码随想录学习,发现我的问题在于:我的回溯出了问题,因为我没有判断左节点和右节点的合理性,所以会出现加入左节点等于null时,回溯停止,但是依据上述代码,path多pop了一个元素,所以要改正代码,需要把push和pop进行配对出现,简单改正后可以AC的代码如下:

class Solution {
public:
    vector<vector<int>> res;
    void dfs(TreeNode* root, int targetSum, vector<int>& path)
    {
        if(root==nullptr) return ;
        path.push_back(root->val); //中
        if(root->left==nullptr&&root->right==nullptr)
        {
            if(root->val==targetSum)
            {
                res.push_back(path);
            }
            return;
        }
        if(root->left)
        {
            dfs(root->left,targetSum-root->val,path);
            path.pop_back(); //这里注意配对
        }
        if(root->right){
            dfs(root->right, targetSum-root->val,path);
            path.pop_back(); //这里也要注意
        }

    }
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<int> path;
        dfs(root,targetSum,path);
        return res;

    }
};

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

这道题目以前做过,但是思路忘记了,再学习代码随想录总结一遍,直接使用索引的效率高:

  • 第一步:如果数组大小为零的话,说明是空节点了。

  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。

  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点

  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

  • 第五步:切割后序数组,切成后序左数组和后序右数组

  • 第六步:递归处理左区间和右区间

详细代码如下:

class Solution {
public:
    TreeNode* dfs(vector<int> inorder, int il,int ir, vector<int> postorder,int pl, int pr)
    {
        //结束条件
        if(il==ir) return nullptr;
        int x = postorder[pr-1];//最后一个元素
        TreeNode* root = new TreeNode(x);
        if(pr-pl==1) return root;
        //找到中序数组中的索引
        int index;
        for(int i=il;i<ir;i++)
        {
            if(inorder[i]==x)
            {
                index=i;
                break;
            }
        }
        //切割中序数组
        int lenr =  index-il; //左子树的长度
        int left_in_beg = il;
        int left_in_end = index;
        int right_in_beg = index+1;
        int right_in_end = ir;

        //切割后序数组
        int left_po_beg = pl;
        int left_po_end = pl+lenr;
        int right_po_beg = left_po_end;
        int right_po_end = pr-1;

        //递归
        root->left = dfs(inorder, left_in_beg, left_in_end, postorder, left_po_beg, left_po_end);
        root->right = dfs(inorder, right_in_beg, right_in_end, postorder, right_po_beg, right_po_end);
        return root; //中
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        //后序遍历
        if(inorder.size()!=postorder.size()) return nullptr;
        return dfs(inorder,0,inorder.size(),postorder,0,postorder.size());

    }
};

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值