代码随想录Day 17 | 110 平衡二叉树 257 二叉树的所有路径 404 左叶子之和

代码随想录Day 17 | 110 平衡二叉树 257 二叉树的所有路径 404 左叶子之和

平衡二叉树

文档讲解:代码随想录
视频讲解: 后序遍历求高度,高度判断是否平衡 | LeetCode:110.平衡二叉树
状态

左右子树的高度差不大于1,高度就采用后序遍历,同样使用递归的解法

  1. 终止条件:当前节点为空时 就返回0
  2. 函数参数:树的所有节点,返回值树的高度或者失效标记
  3. 单层递归功能函数:在一个父节点的位置我们需要做一件事,一个是判断当前父节点的左右子树是不是高度差小于等于1,如果是那么就可以返回当前这个节点的高度给它的父节点。如果不是说明已经不是AVL树了,这时候就需要一个标记来说明,然后返回这个标记给其父节点。当父节点接受到这个标记就知道已经不是AVL,那么后面的求父节点的高度也不需要做了,直接继续返回标记
class Solution {

public:
    int IsAVL(TreeNode* root)
    {
        //终止条件
        if(root == nullptr) return 0;
        //后序遍历
        //左节点
        int left = IsAVL(root->left);
        //如果得到-1的返回说明其子树中已经不满足,则直接返回-1
        if(left == -1) return -1;
        //右节点
        int right = IsAVL(root->right);
        if(right == -1) return -1;

        //单层递归
        //判断左右子树的高度差
        if(abs(left-right) > 1)
        {
            return -1;
        }
        return 1+max(left,right);
    }
public:
    bool isBalanced(TreeNode* root) {
        int res = IsAVL(root);
        if(res == -1) return false;
        return true;
    }
};

利用层序遍历的迭代法:对每个节点进行层序遍历那么求得的就是以该节点为根节点的子树深度,同样也是高度。所以我们可以先定义一个求取深度的函数,然后同样利用层序遍历,对每个节点的左右子节点调用这个函数,然后来比较深度差值。

class Solution {

public:
    int GetDep(TreeNode* cur)
    {
        queue<TreeNode*> treeque;
        if(cur) treeque.push(cur);
        int dep = 0;
        while(!treeque.empty())
        {
            int tempsize = treeque.size();
            for(int i = 0;i<tempsize;i++)
            {
                TreeNode* temp = treeque.front();
                treeque.pop();
                if(temp->left) treeque.push(temp->left);
                if(temp->right) treeque.push(temp->right);
            }
            dep++;
        }
        return dep;
    }
public:
    bool isBalanced(TreeNode* root) {
        queue<TreeNode*> treeque;
        if(root) treeque.push(root);
        while(!treeque .empty())
        {
            int tempsize = treeque.size();
            for(int i=0;i<tempsize;i++)
            {
                TreeNode* cur = treeque.front();
                int left = GetDep(cur->left);
                int right = GetDep(cur->right);
                if(abs(left-right) > 1)
                {
                    return false;
                }
                treeque.pop();
                if(cur->left) treeque.push(cur->left);
                if(cur->right) treeque.push(cur->right);
            }
        }

        return true;
    }
};

二叉树的所有路径

文档讲解:代码随想录
视频讲解: 递归中带着回溯,你感受到了没?| LeetCode:257. 二叉树的所有路径
状态:×

从根节点开始到一个叶子节点的路径,采用前序遍历,当寻找到一条路径,还需要回退到上一个岔路口,选择另一条路进行遍历找到其他的路径,直到所找到的出口是最右边的叶子节点。
递归的三个要素

  1. 参数和返回值:节点,当前的这条路径的所有节点组成的数组path,最终存储结果的数组res
  2. 循环终止条件:当当前节点的左节点和右节点为空时,说明该节点是叶子节点,就说明当前这条路径到头了。
    当到达终止条件时,就应该处理当前字符串的结果了,这时候我们就需要将path中的所有节点值变为字符存入res中
  3. 单层递归
    递归加回溯,递归和回溯不能分开,首先是对中节点的操作就是直接压入path数组中,对于左节点和右节点分别调用递归,如果存在的话。调用递归之后还需要回溯
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 GetPath(TreeNode* root, vector<int> path, vector<string>& res)
    {
        //中节点操作,先操作中节点,是为了叶子节点成为中节点是的加入path
        path.push_back(root->val);
        if(root->left == nullptr && root->right == nullptr)
        {
            string temp;
            for(int i=0;i<path.size()-1;i++)
            {
                temp += to_string(path[i]);
                temp += "->";
            }
            temp += to_string(path[path.size()-1]);
            res.push_back(temp);
            return ;
        }

        //递归左右,回溯
        if(root->left)
        {
            GetPath(root->left,path,res);
            //回溯到父节点 即岔路口
            //path.pop_back();
        }
        if(root->right)
        {
            GetPath(root->right,path,res);
            //path.pop_back();
        }
    }
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<int> temp;
        vector<string> res;
        GetPath(root, temp, res);
        return res;
    }
};

回溯的目的主要要是保证当前节点的数组统计保持不变,这样在向回递归时,可以保持在节点处,所以如果对于传入函数的参数是值传入,那么其实就可以不需要使用回溯。因为对path的修改只是对临时变量的修改并不会改变函数外部的path,相当于用语法特性来完成了回溯。如果是按指针或者引用传递,那么就会对函数外部的path修改为了保持当前节点的path不变,需要手动回溯

左叶子之和

文档讲解:代码随想录
视频讲解: 二叉树的题目中,总有一些规则让你找不到北 | LeetCode:404.左叶子之和
状态:

递归三要素

  1. 返回值和参数,我采用了没有返回值,参数为节点和最终求和的值这两个参数
  2. 终止条件:当前节点为空,因为左或者右叶子节点的判断只能通过父节点来判断
  3. 单层递归逻辑:当前父节点(中)用来存储其子树的左叶子节点和,对于存在左右子节点那么就继续递归调用。
class Solution {
    void GetLeft(TreeNode* root, int& sum)
    {
        if(root == nullptr) return ;
        //对左节点的递归
        if(root->left)
        {
            GetLeft(root->left,sum); 
        }
        //对右节点的递归
        if(root->right)
        {
            GetLeft(root->right,sum);
        }
        //对中节点的计算
        if(root->left !=nullptr && root->left->left == nullptr && root->left->right == nullptr)
        {
            sum += root->left->val;
        }
    }
public:
    int sumOfLeftLeaves(TreeNode* root) {
        int sum = 0;
        GetLeft(root,sum);
        return sum;
    }
};

采用的是左右中的顺序

采用层序遍历
对弹出的节点判断其是否有左叶子节点,如果是就加入

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        queue<TreeNode*> treeque;
        if(root) treeque.push(root);
        int sum = 0;
        while(!treeque.empty())
        {
            int tempsize = treeque.size();
            for(int i=0;i<tempsize;i++)
            {
                TreeNode* cur = treeque.front();
                treeque.pop();
                if(cur->left != nullptr && cur->left->left == nullptr && cur->left->right == nullptr)
                {
                    sum += cur->left->val;
                }
                if(cur->left) treeque.push(cur->left);
                if(cur->right) treeque.push(cur->right);
            }
        }
        return sum;
    }
};
  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值