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

一、110. 平衡二叉树

状态:已解决

1.思路 

        这道题由于要求高度,因此不能从上往下遍历了,而是要从下往上遍历(高度的定义),否则会出错,因此,层序遍历不再适用。我们这里依旧选择后序遍历的做法。

        此题的复杂点在于怎么保存结果,也就是递归函数返回值是什么更合适。如果返回的是子树的高度,那么即使发现了存在不平衡的子树也没有办法返回。如果返回的是对子树是否平衡的判断,那么子树的节点总数也没有办法保存。

        解决这个难点有两种思路:

        (1)返回值为数值,如果有子树不再平衡,就返回-1(也可以是任何正常平衡树不可能出现的深度值),并且每次遍历完一棵就立即判断返回值是否为-1,是的话就一直保持这个返回值返回下去,如果没有出现-1就返回节点总数。

        (2)在参数列表里面设置一个变量,并传它的引用,让该变量保存是否出现过非平衡树的状态。

2.代码实现 

(1)设置-1为标志的方法:

class Solution {
public:
    // 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1
    int getHeight(TreeNode* node) {
        if (node == NULL) {
            return 0;
        }
        int leftHeight = getHeight(node->left);
        if (leftHeight == -1) return -1;
        int rightHeight = getHeight(node->right);
        if (rightHeight == -1) return -1;
        return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
    }
    bool isBalanced(TreeNode* root) {
        return getHeight(root) == -1 ? false : true;
    }
};

(2)添加参数引用保存状态的方法:

class Solution {
public:
    int getDepth(TreeNode* node,bool & judge){
        if(node == NULL) return 0;
        int leftNum=getDepth(node->left,judge);
        if(judge == false) return 0;//judge为false时,说明出现左子树不相等了,此时直接返回
                                    //不用再遍历右子树,相当于剪枝了一下
        int rightNum=getDepth(node->right,judge);
        if(abs(leftNum-rightNum)>1) judge=false;
        if(judge == false) return 0;//judge为false时,说明出现右子树不相等了
        return max(leftNum,rightNum)+1;
    }
    bool isBalanced(TreeNode* root) {
        bool judge = true;
        getDepth(root,judge);
        return judge;
    }
};

二、257.二叉树的所有路径

状态:已解决

1.思路

        这道题是一道标准的回溯题模板,递归和回溯相辅相成,递归主要侧重于每个子过程的处理过程,而回溯主要侧重于利用从子过程回来父级过程中,工作栈进行的一些现场恢复的工作原理。比如,我们传一个形参到递归函数中,那么该参数从子过程回到父级过程时,值也会复原。这就很方便我们保存路径了,甚至于不需要我们专门将前一个路径节点从容器中pop出,例:

        假如我们第一次从A到B的子过程时,用引用字符串保存了"A->B"的路径,后面回溯到A节点的处理过程时,就需要把B从字符串中pop出来,这样才能继续去实现A->C的路径保存。然而如果我们直接将该字符串作为形参传递 ,那么在回到A过程时,该参数会恢复到传递前的值,即形变实不变的原理。

        另外,这道题由于要保存路径,因此只能从上往下遍历,也就是只能前序遍历。

2.代码实现

应用递归三部曲的公式:

(1)确定参数和返回值类型:

void traversal(TreeNode* node,string path,vector<string> & result)

        这里已经有结果的引用参数了,因此无需返回东西。 

(2) 确定终止条件:

if(node->left == NULL && node->right == NULL)

        这里不再是为空才终止了,遍历到叶子节点就要结束,不然由前序遍历的原理空节点也要存入路径中了。

(3)确定单层递归逻辑:

        因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。

path.push_back(node->val);

        然后是递归和回溯的过程,上面说过没有判断node是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。所以递归前要加上判断语句。

        完整代码为:

class Solution {
public:
    void traversal(TreeNode* node,string path,vector<string> & result){
        path += to_string(node->val);//中
        if(node->left == NULL && node->right == NULL){
            result.push_back(path);
            return ;
        }
        if(node->left){
            traversal(node->left,path+"->",result);
        }//左
        if(node->right){
            traversal(node->right,path+"->",result);
        }//右
        return ;
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        if(root == NULL) return result;
        traversal(root,"",result);
        return result;
    }
};

三、404.左叶子之和

状态:已解决

1.思路 

        我是用了一个标志来判断是否是左子树还是右子树的。如果遍历到一个叶子节点,并且标签显示为左子树遍历方式时,就将该节点的值加到结果中,很好理解,但是我不太能看出其遍历顺序。

        老师的讲解为:判断一个节点的左子树是否为一个叶子节点,是的话,由于它是左遍历的叶子节点,那么它就一定是左叶子。采用的是后序遍历,还是因为是在中间结点统计左子树和右子树的左叶子之和。

2.代码实现

(1)我的版本:

class Solution {
public:
    void LeftLeaves(TreeNode* node,int & result,int direction){
        if(node->left == NULL && node->right == NULL && direction == 1){
            result += node->val;
            return;
        }
        if(node->left) LeftLeaves(node->left,result,1);
        if(node->right) LeftLeaves(node->right,result,0);
    }
    int sumOfLeftLeaves(TreeNode* root) {
        int result=0;
        if(root == NULL) return result;
        LeftLeaves(root,result,0);
        return result;
    }
};

(2)老师的版本

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

        int leftValue = sumOfLeftLeaves(root->left);    // 左
        
        int rightValue = sumOfLeftLeaves(root->right);  // 右

        if (root->left && !root->left->left && !root->left->right) { // 左子树就是一个左叶子的情况
            leftValue = root->left->val;
        }
        int sum = leftValue + rightValue;               // 中
        return sum;
    }
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值