代码随想录算法训练营第17天 |LeetCode110.平衡二叉树 257. 二叉树的所有路径 404.左叶子之和


110. 平衡二叉树

  拿到这题,我们首先要明确平衡二叉树的概念:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 

既然要判断它是不是平衡二叉树,我们就要遍历这棵树,我们这里采用递归的方式来遍历

遇到要使用递归函数的地方,先上递归三部曲!

1.确定函数返回值和参数:

我们要看它是不是平衡二叉树,我们就要得到他每一条分支的深度,所以我建议将返回值设置为int,而其参数,我们暂定为TreeNode*

2.确定递归函数的终止条件:

if(node==nullptr){
       return 0;
}

3.确定单层递归的逻辑:

要确定逻辑,我们就要确定我们遍历二叉树的方式(前中后序遍历)的哪种方式,这里我采用的是后序遍历的方式。

我们应该在得到了左右子树的高度之后,我们该用什么方法来判断它的高度差是不是符合要求的呢?        如果高度差小于1,我们返回当前二叉树的高度,否则返回-1,这个返回的-1表示它已经不是一个平衡二叉树了

该部分具体代码如下

int leftHeight = getHeight(node->left); // 左
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right); // 右
if (rightHeight == -1) return -1;

int result;
if (abs(leftHeight - rightHeight) > 1) {  // 中
    result = -1;
} else {
    result = 1 + max(leftHeight, rightHeight); // 以当前节点为根节点的树的最大高度
}

return result;

完整代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int getdepth(TreeNode* node){
        if(node==nullptr){
            return 0;
        }
        
        int leftdep=getdepth(node->left);
        if(leftdep==-1)     return -1;
        int rightdep=getdepth(node->right);
        if(rightdep==-1)    return -1;
        if(abs(leftdep-rightdep)>1)   return -1;
        else return max(leftdep,rightdep)+1;
    }
    bool isBalanced(TreeNode* root) {
        if(root==nullptr)   return true;
        return getdepth(root)==-1? false :true;
    }
};


257. 二叉树的所有路径

本题是让我们求二叉树的所有路径,比如一棵这样的二叉树

那么他的路径就是“1->2->5” 和 "1->3"

想要获得其路径,我们就要遍历二叉树,我们考虑使用递归的方式来遍历这棵二叉树,那么我们应该如何写递归函数呢?

先上递归三部曲:

1.确定函数参数和返回值:

因为我们要返回的是vector<string>类型,我认为将函数返回值设置为void比较方便,这样我们在函数参数中定义一个vector<string>的参数:result就可以方便地增删其内部的元素了

2.确定递归函数的终止条件 

一般情况下,我们都是这么写函数的终止条件的:

if(node==NULL)    return;

但是我不建议在这里使用这样的写法,为什么?因为当我们找到叶子节点时,就可以结束并且处理结果逻辑了,将路径添加到result

什么是叶子节点?该节点不为空,并且其左右孩子都为空时就是我们要找到的叶子节点

所以我们在这里应该这样写递归的终止条件:

if(node->left==NULL&&node->right==NULL){
    处理逻辑
}

最终处理的逻辑如下:

if (node->left == NULL && node->right == NULL) { // 遇到叶子节点
    string sPath;
    for (int i = 0; i < path.size() - 1; i++) { // 将path里记录的路径转为string格式
        sPath += to_string(path[i]);
        sPath += "->";
    }
    sPath += to_string(path[path.size() - 1]); // 记录最后一个节点(叶子节点)
    result.push_back(sPath); // 收集一个路径
    return;
}

3.确定单层递归的逻辑:

我们先得明确是用什么样的遍历方式,才能写好递归逻辑,这题使用的是前序遍历,那么我们为什么要使用前序遍历呢?因为只用这样,我们才能按照“由父到子”的节点顺序,输出二叉树的一条路径,那么有小伙伴说了,使用后序遍历,然后reverse一下result不行吗?我的回答是,如果使用reverse函数,那么箭头->又该怎么办呢?

确定了要使用前序遍历,我们再来讲讲本题要用到的另一个知识点:回溯算法

为什么这里会用到回溯算法呢?

让我们先来简单介绍一下回溯算法,通俗的来讲,回溯算法就是一条路走到头,一步步得回退,直到回退到原位置(在这一步步的回退中,记录下每一次的回退内容),再重新开始走新的一条路。

我们还是拿出这棵二叉树来举例

回溯算法的实例模拟过程:先从头结点“1”开始走,一边走一边记录遇到的节点,直到叶子节点“5”时,我们就拿到了一条完整的路径了“1->2->5”,此时我们开始回退,将记录下来的5弹出,再回退,将2弹出,再回退到头结点时,又开始新的一条路径,一直走,遇到叶子节点“3”时停止,得到一条完整路径“1->3”弹出3,再回退到头结点1。

看到了回溯算法的简单介绍之后,相信大家也明白了,为什么这里要用到回溯算法,因为只有这样我们才能重复使用到“1”这个节点——左右子树的交点,正确得返回其完整路径。

我们又应该如何写回溯的代码呢?我们要牢记一点:回溯和递归是一一对应的,有一个递归,就要有一个回溯

所以回溯部分代码如下:

if (cur->left) {
    traversal(cur->left, path, result);
    path.pop_back(); // 回溯
}
if (cur->right) {
    traversal(cur->right, path, result);
    path.pop_back(); // 回溯
}

 很多同学看到了回溯弹出的操作之后,又开始晕了,不理解递归是如何进行了。在本题中,我抽象的概括为:看到调用递归的逻辑,则说明它将直接走到二叉树的底部(即叶子节点),并且它的下一行代码不会立即执行,而是等到做完全部的递归时才会执行。

本题的完整代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void traversal(TreeNode* node,vector<int>&path,vector<string>&result){
        path.push_back(node->val);
        
        if(node->left==nullptr&&node->right==nullptr){
            string spath;
            for(int i=0;i<path.size()-1;i++){
                spath+=to_string(path[i]);
                spath+="->";
            }
            spath+=to_string(path[path.size()-1]);
            result.push_back(spath);
            return;
        }
        if(node->left){
            traversal(node->left,path,result);
            path.pop_back();
        }
        if(node->right){
            traversal(node->right,path,result);
            path.pop_back();
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string>result;
        vector<int>path;
        traversal(root,path,result);
        return result;
    }
};


404. 左叶子之和

我们要求左子叶之和,我们就要遍历二叉树,我采用递归遍历的方式:

先上递归三部曲:

1.确定函数参数和返回值:

返回值为int型,函数参数暂定为TreeNode*

2.确定递归函数的终止条件:

当root==NULL时返回0;

3.确定单层递归的逻辑:

先明确遍历方式为后序遍历(左右中)

那么我们应该如何确定遍历到的节点是否为左节点呢?

当该节点为左节点并且不为空,其下一个左节点为空,下一个右节点为空是,说明该节点为左子叶。所以函递归函数具体代码如下:

nt getleftnode(TreeNode* root){
        if(root==nullptr){
            return 0;
        }
        int leftnum=getleftnode(root->left);//左
        if(root->left!=NULL&&root->left->left==NULL&&root->left->right==NULL)   
            leftnum=root->left->val;//确定其为左子叶
        int rightnum=getleftnode(root->right);//右
        int result=leftnum+rightnum;
        return result;
    }

整体代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int getleftnode(TreeNode* root){
        if(root==nullptr){
            return 0;
        }
        int leftnum=getleftnode(root->left);//左
        if(root->left!=NULL&&root->left->left==NULL&&root->left->right==NULL)   
            leftnum=root->left->val;
        int rightnum=getleftnode(root->right);//右
        int result=leftnum+rightnum;
        return result;
    }
    int sumOfLeftLeaves(TreeNode* root) {
        if(root==NULL)  return 0;
        if (root->left == NULL && root->right== NULL) return 0;
        return getleftnode(root);
    }
};

精简后代码如下:

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == NULL) return 0;
        int leftValue = 0;
        if (root->left != NULL && root->left->left == NULL && root->left->right == NULL) {
            leftValue = root->left->val;
        }
        return leftValue + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值