代码随想录笔记|C++数据结构与算法学习笔记-二叉树(三)|LeetCode101.对称二叉树、104.二叉树的最大深度、111.二叉树的最小深度

文章详细解析了对称二叉树的判断方法、后序遍历在解决此类问题中的重要性,以及如何通过递归实现二叉树的最大深度和最小深度计算,包括伪代码和C++代码示例。
摘要由CSDN通过智能技术生成

本文基于代码随想录的文章和视频,写入一些小小的个人理解

101.对称二叉树

思路

整体思路

判断是否是二叉树,本质上是判断根结点的左子树和右子树是否可以相互翻转。这里其实和之前将的翻转二叉树非常类似。

即根结点之外的所有左右结点都应该对称于中轴线进行比较,这样就要求我们要收集每个结点的左右孩子信息。

确定遍历顺序

二叉树类的题目,确定遍历顺序是非常重要的。其实这道题只能使用后序。

因为我们要不断收集左右孩子的信息不断返回给上一个结点,这样我们才能知道以左结点为根结点的数和以右结点为根结点的数是否是相互翻转的。因为只有后序遍历我们才能把左右孩子的信息返回给上一层

伪代码

这里用递归遍历

  • 确定传参和返回值
bool compare(TreeNode* left, right)
  • 确定终止条件

节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点

  1. 左节点为空,右节点不为空,不对称,return false

  2. 左不为空,右为空,不对称 return false

  3. 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

  1. 左右都不为空,比较节点数值,不相同就return false

此时左右节点不为空,且数值也不相同的情况我们也处理了。

if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false; // 注意这里我没有使用else

注意上面最后一种情况,我没有使用else,而是else if, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。

  • 确定单层递归的逻辑

    此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

    • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
    • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
    • 如果左右都对称就返回true ,有一侧不对称就返回false 。
bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
bool isSame = outside && inside;                    // 左子树:中、 右子树:中(逻辑处理)
return isSame;

如上代码中,我们可以看出使用的遍历方式,左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)。

CPP代码

class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right) {
        // 首先排除空节点的情况
        if (left == NULL && right != NULL) return false;
        else if (left != NULL && right == NULL) return false;
        else if (left == NULL && right == NULL) return true;
        // 排除了空节点,再排除数值不相同的情况
        else if (left->val != right->val) return false;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
        bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
        bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)
        return isSame;

    }
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }
};

104.二叉树的最大深度

思路

先给结论,二叉树的高度就是二叉树的最大深度。

深度和高度及其遍历顺序

求高度用的是后序遍历,求深度用的是前序遍历

  • 求高度用的是后序遍历:求高度是从下往上来记数的,后序遍历是做左右中,所以说我们最后遍历到父结点也就是中结点的时候才进行+1,符合我们求高度的逻辑
  • 求深度用的是前序遍历:求深度是从上往下来计数,前序遍历是做中左右,所以我们往下到中结点就+1,符合我们求深度的逻辑。

伪代码实现

  • 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
int getdepth(TreeNode* node)
  • 确定终止条件:如果为空节点的话,就返回0,表示高度为0.
if (node == NULL) return 0;
  • 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
int leftdepth = getdepth(node->left);       // 左
int rightdepth = getdepth(node->right);     // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;

CPP代码

class solution {
public:
    int getdepth(TreeNode* node) {
        if (node == NULL) return 0;
        int leftdepth = getdepth(node->left);       // 左
        int rightdepth = getdepth(node->right);     // 右
        int depth = 1 + max(leftdepth, rightdepth); // 中
        return depth;
    }
    int maxDepth(TreeNode* root) {
        return getdepth(root);
    }
};

精简后的C++代码

class solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == null) return 0;
        return 1 + max(maxDepth(root->left), maxDepth(root->right));
    }
};

111.二叉树的最小深度

思路

和最大深度的区别

最小深度的定义:根结点到最近的叶子结点的距离才是最小深度;

还记得最大深度我们求的是二叉树的高度,那么我们求二叉树的最小高度也就能求出其最小深度。所以我们可以用后序遍历来求二叉树的最小深度

遍历顺序

那么为什么不用前序遍历呢,前序好像更符合逻辑一点,其实就是前序的代码太复杂了,不合适。

伪代码实现

  • 确定参数和返回值
int getheight(TreeNode* node){
  
}
  • 明确终止条件
if (node == NULL) return 0;
  • 处理递归的单层逻辑
int leftheight = getheight (node->left);
int rightheight = getheight (node->right);
if (node->left == NULL && node->right != NULL)
  return 1 + right;
if (node->left != NULL && node->right == NULL)
  return 1 + left;

result = 1 + min(leftheight, rightheight);
return result;

注意,最后的处理逻辑一定不要写int depth = 1 + min(leftdepth, rightdepth);,这样的话我们就会把左右孩子一个为空,一个不为空的情况记录进去。并不是去取左右子树的最小值。具体案例可以看卡哥视频:看起来好像做过,一写就错! | LeetCode:111.二叉树的最小深度

CPP代码

class Solution {
public:
    int getDepth(TreeNode* node) {
        if (node == NULL) return 0;
        int leftDepth = getDepth(node->left);           // 左
        int rightDepth = getDepth(node->right);         // 右
                                                        // 中
        // 当一个左子树为空,右不为空,这时并不是最低点
        if (node->left == NULL && node->right != NULL) { 
            return 1 + rightDepth;
        }   
        // 当一个右子树为空,左不为空,这时并不是最低点
        if (node->left != NULL && node->right == NULL) { 
            return 1 + leftDepth;
        }
        int result = 1 + min(leftDepth, rightDepth);
        return result;
    }

    int minDepth(TreeNode* root) {
        return getDepth(root);
    }
};

精简后的代码:

class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == NULL) return 0;
        if (root->left == NULL && root->right != NULL) {
            return 1 + minDepth(root->right);
        }
        if (root->left != NULL && root->right == NULL) {
            return 1 + minDepth(root->left);
        }
        return 1 + min(minDepth(root->left), minDepth(root->right));
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值