代码随想录算法训练营打卡Day17 | LeetCode110 平衡二叉树、LeetCode257 二叉树的所有路径、LeetCode404 左叶子之和

摘要

  • 平衡二叉树的左右子树的高度差不超过1
  • 求二叉树的所有路径是回溯法的基础
  • 判断当前节点是不是左叶子

LeetCode110 平衡二叉树

110. 平衡二叉树 - 力扣(Leetcode)

  • 复习二叉树的高度和深度概念,借用代码随想录里的例子
image.png
  • 关键概念

    • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)

    • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

    • 从0开始为边的条数,从1开始为节点数

  • 二叉树的深度,在数值上等于二叉树的根节点的高度。

    • 求深度是从上往下计算的,所以前序遍历的“中左右”对应着求深度的由父节点到子节点,即先计算父节点的深度(根节点的深度为1),然后将父节点的深度传递给左右子树。
    • 求高度是从下往上计算的,所以后序遍历的“左右中”对应着求高度的先分别求左右子树的高度再返回给父节点
    • 二叉树的深度,在数值上等于二叉树根节点的高度,所以二叉树的深度也可以用后序遍历计算。
    • 但真正求二叉树的深度的逻辑,应该是前序遍历。
  • 平衡二叉树要通过每个节点的左右子树的高度来判断,而不是根节点的左右子树的高度差不超过1就是平衡二叉树,所以平衡二叉树也是要递归遍历每个节点的

image.png

上图为反例,上图所示的树不是平衡二叉树

  • 求高度用后序遍历,并且需要在求高度的过程中判断当前节点的左右子树是不是平衡二叉树,如果当前节点的左右子树不是平衡二叉树,那当前树也不是平衡二叉树

递归法的题解代码如下

class Solution {
public:
    int getAVLDepth(TreeNode* root) {
        if (!root) return 0;

        int leftDepth = getAVLDepth(root->left);
        if (leftDepth == -1) return -1;
        int rightDepth = getAVLDepth(root->right);
        if (rightDepth == -1) return -1;
        
        if (abs(leftDepth - rightDepth) > 1) return -1;
        int depth = 1 + max(leftDepth, rightDepth);
        return depth;
    }
    bool isBalanced(TreeNode* root) {
        return getAVLDepth(root) != -1;
    }
};

LeetCode257 二叉树的所有路径

257. 二叉树的所有路径 - 力扣(Leetcode)

  • 这里需要计算的是从根节点到叶子节点的路径。
    • 到达一个叶节点后,应该回溯到最近的一个非叶节点,再继续寻找未被访问的叶节点。
    • 如此递归直到访问完所有叶节点。
    • 从根节点开始,所以是根(父)节点先被处理,这里应该采用前序遍历“中左右”
  • 递归法按如下三步确定代码的思路:
    • 递归函数的参数和返回值:当前节点;当前保存的路径,保存答案路径的数组。
    • 递归函数的终止条件:到达叶节点
    • 单层递归的逻辑:将当前节点加入当前路径中,若当前节点为叶节点,则将当前路径存入答案数组后返回;若当前节点不为叶节点,则继续递归寻找叶节点,递归后将加入路径的节点弹出来完成回溯。
  • 回溯与递归是一一对应,成对存在的。有一次递归,就应该有一次回溯。求二叉树的所有路径是回溯法的基础。

清晰的体现回溯法的题解代码如下

class Solution {
public:
    void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
        path.push_back(cur->val); // 中,先将当前节点放入当前路径
        
        // 终止条件,当前节点为叶节点时将当前路径放入答案数组
        if (!cur->left && !cur->right) { 
            string sPath;
            int size = path.size();
            for (auto& iter : path) {
                sPath += to_string(iter);
                if (--size) sPath += "->";
            }
            result.push_back(sPath);
            return;
        }
        
        // 向左找叶节点
        if (cur->left) {
            traversal(cur->left, path, result);
            path.pop_back();// 回溯,将当前节点的左孩子弹出
        }
        
        // 向右找叶节点
        if (cur->right) {
            traversal(cur->right, path, result);
            path.pop_back();// 回溯,将当前节点的右孩子弹出
        }

    }

    vector<string> binaryTreePaths(TreeNode* root) {
        vector<int> path;
        vector<string> result;
        if (root) traversal(root, path, result);
        return result;
    }
};
  • 通过传入参数不为引用,从而能隐式地实现回溯,代码如下。
class Solution {
public:
    void traversal(TreeNode* cur, string path, vector<string>& result) {
        path += to_string(cur->val);
        if (!cur->left && !cur->right) {
            result.push_back(path);
            return;
        }
        if (cur->left) traversal(cur->left, path + "->", result);
        if (cur->right) traversal(cur->right, path + "->", result);

    }

    vector<string> binaryTreePaths(TreeNode* root) {
        string path;
        vector<string> result;
        if (root) traversal(root, path, result);
        return result;
    }
};

LeetCode404 左叶子之和

404. 左叶子之和 - 力扣(Leetcode)

  • 首先明确左叶子的定义:当前节点为叶节点,且为其父节点的左孩子。仅凭当前节点的保留的信息,只能判断当前节点是否是叶节点;只有通过当前节点的父节点,才能判断当前节点是否为左孩子。
  • 初见题目时采用了前序遍历,直观理解为先。
    • 递归函数的参数和返回值:参数为当前父节点,当前左叶子之和。无返回值。
    • 递归的终止条件:前序遍历没有明显的终止条件,隐含在各个if的判空逻辑里。或者明显的写出当前节点为叶节点.
    • 单层递归的处理逻辑:判断当前节点有左叶子,然后再去判断当前节点的左孩子是否有左叶子,右孩子是否有左叶子

前序遍历的题解代码如下

class Solution {
public:
    void sumOfLeftLeavesWorkPlace(TreeNode* root, int& sum) {
        if (root->left && !root->left->left && !root->left->right) {
            sum += root->left->val;
        }
        else if (root->left) sumOfLeftLeavesWorkPlace(root->left, sum);
        if (root->right) sumOfLeftLeavesWorkPlace(root->right, sum);
    }

    int sumOfLeftLeaves(TreeNode* root) {
        int sum = 0;
        if (root) sumOfLeftLeavesWorkPlace(root, sum);
        return sum;
    }
};
  • 代码随想录的讲解采用的是后序遍历,其实这题只需要将左叶子统计出来就可以了,采用不同的遍历方式对应着不同的直观理解
    • 前序遍历:以当前节点为父节点,判断当前节点是否有左叶子,再去判断当前节点的左孩子和右孩子是否有左叶子。自上而下
    • 后序遍历:找到左叶子后,将左叶子的值返回给左叶子的父节点的那层递归,若左子树就是左叶子则直接取值(递归的终止条件之一);然后找右子树的左叶子。

后序遍历的实现代码

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

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

        int sum = leftValue + rightValue;               // 中
        return sum;
    }
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容