代码随想录算法训练营第十五天|二叉树遍历题目

注意:在做二叉树的题目的时候,思考步骤是先确定二叉树的遍历方式,然后就是在遍历的时候如何操作

101. 对称二叉树

题目链接:101. 对称二叉树 - 力扣(LeetCode)

给你一个二叉树的根节点 root , 检查它是否轴对称。

解题思路:

本题,在递归遍历的过程中,要同时遍历两棵树。比较两个子树的里侧和外侧的元素是否相等。

class Solution {
public:
    bool compare(const TreeNode* left, const TreeNode* right) {

        if (left == NULL && right == NULL) {
            // 左右都为空
            return true;
        } else if (left == NULL || right == NULL) {
            // 左右有空
            return false;
        } else if (left->val != right->val) {
            // 左右都不为空,值不相等
            return false;
        }
        //左右都不为空,且值相等
        // 此时才做递归,做下一层的判断
        bool outer = compare(left->left,right->right);//外层节点
        bool in = compare(left->right,right->left);//内层节点
        return outer && in;
    }
    bool isSymmetric(TreeNode* root) {
        if (root == NULL)
            return false;
        return compare(root->left, root->right);
    }
};

572.另一棵树的子树

题目链接:572. 另一棵树的子树 - 力扣(LeetCode)

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

解题思路:

遍历主树(root)的每一个节点,对于每个节点,我们都尝试检查该节点是否以子树(subRoot)为根的子树。

class Solution {
public:
    bool isSame(TreeNode* root,TreeNode* subRoot){
        if(!root && !subRoot)return true;// 两个节点都为空,则相同 
        if(!root || !subRoot || root->val != subRoot->val)return false;// 其中一个节点为空,则不相同

        // 递归检查左子树和右子树
        return isSame(root->left,subRoot->left) && isSame(root->right,subRoot->right);
    }
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if(root == NULL)return false;// 如果s为空,则不包含t
        // 检查当前节点为根的子树是否与t相同 
        if(isSame(root,subRoot))return true;
        // 递归地在左子树和右子树中查找
        return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);

    }
};

404.左叶子之和

题目链接:404. 左叶子之和 - 力扣(LeetCode)

给定二叉树的根节点 root ,返回所有左叶子之和。

解题思路:遍历二叉树,累计求和左叶子

关键点在于如何判断左叶子。左叶子 == 左节点 + 叶子节点

叶子节点好判断,左右孩子为空。我们需要如何确保它是左叶子。就必须要通过节点的父节点来判断其左孩子是不是左叶子。

如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子。

class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        int sum = 0;
        if (root == NULL)
            return 0;

        // 如果当前节点是左叶子
        if (root->left && !root->left->left && !root->left->right) {
            sum += root->left->val;// 将该左子节点的值加入总和
        }

        // 递归地计算左子树和右子树中所有左叶子节点的值之和
        sum += sumOfLeftLeaves(root->left);
        sum += sumOfLeftLeaves(root->right);

        return sum;
    }
};

平时我们解二叉树的题目时,已经习惯了通过节点的左右孩子判断本节点的属性,而本题我们要通过节点的父节点判断本节点的属性。

希望通过这道题目,可以扩展大家对二叉树的解题思路。

513.找树左下角的值

题目链接:513. 找树左下角的值 - 力扣(LeetCode)

给定一个二叉树,在树的最后一行找到最左边的值。

解题思路:

注意,本题找到的是最左边的值,不是左叶子。本题采用层次遍历就很容易解决,就不加赘述了。

问题1:如何确保最后一行?

树的最后一行 ,也就是要找深度最大的叶子节点。可以采用前序遍历求二叉树的深度,找到最大深度,然后返回。

问题2:那么如何确保找到的是最左边的叶子节点?

前序遍历是中左右,所以我们是先处理左节点再处理右节点。也就是说在同一层深度中,我们是先访问的左节点,再访问右节点。我们只要保证先左再右,就可以了。

class Solution {
public:
    int maxDepth; // 记录最大深度
    int firstLeft; // 记录最底层最左边节点的值

    
    void find(TreeNode* cur, int depth) {
        if (!cur) // 如果当前节点为空,则直接返回
            return;
        
        if (depth > maxDepth) { // 如果当前节点的深度大于已记录的最大深度
            maxDepth = depth; // 更新最大深度
            firstLeft = cur->val; // 记录当前节点的值作为最底层最左边节点的值
        }
        
        // 递归地处理左子树和右子树
        find(cur->left, depth + 1);
        find(cur->right, depth + 1);
    }

    
    int findBottomLeftValue(TreeNode* root) {
        maxDepth = INT_MIN; // 初始化最大深度为最小整数值
        find(root, 1); // 调用find函数从根节点开始查找
        return firstLeft; // 返回最底层最左边节点的值
    }
};

106.从中序与后序遍历序列构造二叉树

题目链接:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

解题思路:

  1. 后序遍历的特性:后序遍历的顺序是先遍历左子树,然后遍历右子树,最后访问根节点。因此,后序遍历的最后一个元素总是当前遍历子树的根节点。

  2. 中序遍历的特性:中序遍历的顺序是先遍历左子树,然后访问根节点,最后遍历右子树。这意味着,如果我们知道了根节点在中序遍历中的位置,我们就可以将中序遍历数组分割成左子树和右子树两部分。

详细步骤

  1. 检查输入:首先检查输入的中序遍历和后序遍历数组是否为空。如果任一为空,则无法构建二叉树,返回None

  2. 找到根节点:后序遍历的最后一个元素是当前子树的根节点。

  3. 分割中序遍历:在中序遍历数组中找到根节点的位置,这个位置将中序遍历数组分为左子树和右子树两部分。

  4. 分割后序遍历:根据中序遍历的分割点,将后序遍历数组(除了最后一个元素)也分割成左右两部分。注意,后序遍历的右子树部分不包括根节点。

  5. 递归构建:使用分割后的中序遍历和后序遍历数组,递归地构建左子树和右子树。

  6. 返回根节点:将构建好的左子树和右子树连接到根节点上,并返回根节点。

注意:在分割的时候,确定切割的标准,是左闭右开,还有左开右闭,还是左闭右闭,这个就是不变量,要在递归中保持这个不变量。

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        // 如果后序遍历序列为空,则返回空指针
        if (postorder.empty()) return nullptr;

        // 创建一个新的树节点,其值为后序遍历序列的最后一个元素(根节点)
        TreeNode* root = new TreeNode(postorder.back());

        // 如果后序遍历序列只有一个元素,则直接返回创建的根节点
        if (postorder.size() == 1) return root;

        // 在中序遍历序列中找到根节点的位置
        auto index = find(inorder.begin(), inorder.end(), root->val);

        // 分割中序遍历序列得到左子树和右子树的中序遍历序列
        vector<int> leftInorder(inorder.begin(), index);
        vector<int> rightInorder(index + 1, inorder.end());

        // 根据左子树的中序遍历序列的长度,在后序遍历序列中分割得到左子树和右子树的后序遍历序列
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end() - 1);

        // 递归地构建左子树和右子树
        root->left = buildTree(leftInorder, leftPostorder);
        root->right = buildTree(rightInorder, rightPostorder);

        // 返回根节点
        return root;
    }
};

补充:

前序和中序可以唯一确定一棵二叉树。

后序和中序可以唯一确定一棵二叉树。

前序和后序不能唯一确定一棵二叉树!,因为没有中序遍历无法确定左右部分,也就是无法分割。

总的来说,中序和前序或者后序两两组合可以确定一棵二叉树。

题外话:本题给我的感觉和101.对称二叉树 有点像,都是在一次遍历中,同时处理左右子树。

代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值