代码随想录第十七天LeetCode 110、257、404

110. 平衡二叉树

在这里插入图片描述
首先看到定义,高度平衡的二叉树指一棵二叉树每个结点的左右子树的高度差不大于1。所以自然的想到用后序遍历求结点高度,并进行对比判断是否是一棵高度平衡的二叉树,如果有一个结点下的左右子树不是高度平衡的,那么整颗树都不是。

  • 递归
    递归的后序遍历,处理结点的顺序是左右中,下面来看递归三部曲:

    1. 输入输出:输入是结点,输出是结点高度为int
    2. 终止条件:如果结点为空,返回0(return 0就代表遍历到空结点,空结点的高度为0);如果结点对比的差大于1,返回-1,然后层层向上返回-1,这里使用-1来表示不平衡的情况,所以最后主函数获取到递归最后的输出为-1的话表示不平衡;
    3. 单层递归逻辑:按照左右中的顺序处理,这里中就是处理中结点的左右子树的高度,函数体最后返回值也是结点的高度。

    下面看具体的递归函数:

    int getdepth(TreeNode* cur){
        if(cur == NULL) return 0;
        int leftdep = getdepth(cur->left); //左
        if(leftdep == -1) return -1; //子树不是直接返回
        int rightdep = getdepth(cur->right); //右
        if(rightdep == -1) return -1;
        if(abs(leftdep - rightdep) > 1) return -1;
        else{
            return 1 + max(leftdep,rightdep); //中
        }
    
    }
    

    这里需要注意,在处理完左右节点后,需要立即判断这颗结点下的子树是否平衡,如果不平衡直接返回-1,否则会被后续的返回值覆盖,从而导致不能输出正确的结果。换句话说也就是如果有一个结点下的左右子树不是高度平衡的,那么整颗树都不是

如果遇到一个结点的高度为-1,那么直到返回到根节点的左右孩子时,左孩子或右孩子的高度被记为-1,这样另一个孩子即使是平衡的且高度为1,二者的差值也是2,不符合条件,最后根节点返回-1。所以最后主函数中判断条件为根节点高度是否等于-1。

最后是完整代码:

class Solution {
public:
    int getdepth(TreeNode* cur){
        if(cur == NULL) return 0;
        int leftdep = getdepth(cur->left);
        if(leftdep == -1) return -1;
        int rightdep = getdepth(cur->right);
        if(rightdep == -1) return -1;
        if(abs(leftdep - rightdep) > 1) return -1;
        else{
            return 1 + max(leftdep,rightdep);
        }

    }
    bool isBalanced(TreeNode* root) {
        return getdepth(root) >= 0 ? true : false;
    }
};

本题迭代很麻烦,跳过!

257. 二叉树的所有路径

本题涉及到回溯,并且为了方便理解回溯的过程,卡哥在代码中显式的写了出来。因为路径是自上而下的,所以很明显要使用前序遍历,这里使用递归的前序遍历来写。这里需要额外写一个递归函数来在主函数中调用。

  1. 输入输出:输入参数有三个,分别是结点TreeNode*、存放单条路径的容器vector<int>、存放结果的容器vector<string>;输出的结果类型是vector<string>
  2. 终止条件:当结点为叶子结点时,输入答案;
  3. 单层递归逻辑:前序遍历的顺序为中左右,先输入中间结点的值,再处理左右结点。
void travesal(TreeNode* cur , vector<int>& path , vector<string>& ans){
        string s;
        path.push_back(cur->val);
        if(cur->left == NULL && cur->right == NULL){
            for(int i = 0; i < path.size(); i++){
                s.append(to_string(path[i]));
                if(i != path.size() - 1) s.append(sign);
            }
            ans.push_back(s);
            return;
        }
        if(cur->left){
            travesal(cur->left, path, ans);
            path.pop_back();
        }
        if(cur->right){
            travesal(cur->right, path, ans);
            path.pop_back();
        }
    }

需要注意几个细节:

  • 中结点的处理需要放在终止条件判断的前面,因为终止条件是判断当前结点是否为叶子结点,如果放在后面就会缺少叶子结点的输入;
  • 输入参数中记录路径使用的是vector<int>类型,这样输入的时候比较方便,最后在输出完整路径的时候进行类型转换,涉及到一些字符串处理函数;
  • 回溯的逻辑加在了处理左右结点中,注意回溯是在一条路径处理完并返回之后再逐层回溯,并不是输入一个就pop一个元素。

然后是主函数的调用:

	string sign = "->";
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> ans;
        vector<int> path;
        travesal(root, path, ans);
        return ans;
    }

这里我使用了一个string sign = "->";来处理题目要求,也可以在递归函数体处理输出的时候使用运算符重载:path += "->"

下面有使用string path直接作为路径字符串作为输入参数的代码:

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

这里回溯隐藏在了traversal(cur->left, path + "->", result);中:递归函数的输入参数是string path,也就是值传递,不是地址传递,也就是说path的值在每一层递归中是不一样的,具体可以在代码中添加标准输出:

void traversal(TreeNode* cur, string path, vector<string>& result) {
        path += to_string(cur->val); // 中
        std::cout<<cur->val<<":"<<path<<std::endl;
        if (cur->left == NULL && cur->right == NULL) {
            result.push_back(path);
            return;
        }
        if (cur->left) traversal(cur->left, path + "->", result); // 左
        std::cout<<cur->val<<":"<<path<<std::endl;
        if (cur->right) traversal(cur->right, path + "->", result); // 右
        std::cout<<cur->val<<":"<<path<<std::endl;
    }

输出如下:
在这里插入图片描述
可以清晰的看出在不同结点下的路径输出是不一样的。
迭代法没写!

404. 左叶子之和

在这里插入图片描述
本题首先要弄清楚什么是左叶子,首先它是一个叶子结点,其次他是其父节点的左孩子。不难发现,判断一个结点是否为叶子结点,靠他本身是不行的,因为这样不能知道他是不是左孩子,所以本题要使用叶子结点的父节点来判断。
其实,层序遍历是最直白简单的,只要在处理结点时加上判断条件即可。

层序遍历的代码如下:

int sumOfLeftLeaves(TreeNode* root) {
        stack<TreeNode*> st;
        int sum = 0;
        st.push(root);
        while(!st.empty()){
            TreeNode* cur = st.top();
            st.pop();
            if(cur->left) st.push(cur->left);
            if(cur->left != NULL && cur->left->left == NULL && cur->left->right == NULL){
                sum += cur->left->val;
            }
            if(cur->right) st.push(cur->right);
        }
        return sum;
    }

其中if(cur->left != NULL && cur->left->left == NULL && cur->left->right == NULL)就是判断一个结点是否为左叶子的判断条件,这个条件同样适用递归。

  • 递归
    递归这里使用后序遍历,因为返回值需要最后处理。

    1. 输入输出:输入为结点,输出为sum是int类型
    2. 终止条件:结点为空
    3. 单层递归逻辑:左右中的顺序,当遇到结点为左叶子时,将值加入到sum中

    代码如下:

int sumOfLeftLeaves(TreeNode* root) {
        if(root == NULL) return 0;
        int left = sumOfLeftLeaves(root->left); //左
        if(root->left && root->left->left == NULL && root->left->right == NULL){
            left += root->left->val;
        }
        int right = sumOfLeftLeaves(root->right); //右
        return left + right; //中
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值