算法日记day 17(二叉树的最大、最小深度)

一、二叉树的最大深度

题目:

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:3

示例 2:

输入:root = [1,null,2]
输出:2

思路:

采用后序遍历的方法,一层一层的去判断子节点与父节点的距离,可以将一整个二叉树转变为一个个独立开来的二叉树,然后分别判断距离,在逐渐返回上一层,直到返回到根节点为止

代码:

public int maxDepth(TreeNode root) {
    // 如果根节点为空,即空树,深度为 0
    if (root == null) return 0;
    
    // 递归计算左子树的最大深度
    int leftDepth = maxDepth(root.left);
    
    // 递归计算右子树的最大深度
    int rightDepth = maxDepth(root.right);
    
    // 当前节点的深度为左右子树最大深度的较大值加上当前节点的深度 1
    int depth = Math.max(leftDepth, rightDepth) + 1;
    
    // 返回当前节点的深度
    return depth;
}
  • 首先进行判断,如果 root 为 null,即空树,则深度为 0。
  • 否则,分别递归计算左子树 root.left 和右子树 root.right 的最大深度。
  • 使用 Math.max 方法比较左右子树的最大深度,然后加上当前节点的深度 1,得到当前节点为根的子树的最大深度 depth
  • 最后返回计算得到的 depth 值作为结果。
  • maxDepth 方法通过递归调用自身,深度优先地计算每个节点的深度。
  • 如果树为空,则最大深度为 0。
  • 否则,通过递归分别计算左右子树的深度,并根据子树深度的比较确定当前子树的最大深度。
  • 返回的最终深度值代表整棵树的最大深度。

 此外代码还可以进行简化

public int maxDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}

二、二叉树的最小深度

题目:

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明:叶子节点是指没有子节点的节点。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:2

示例 2:

输入:root = [2,null,3,null,4,null,5,null,6]
输出:5

思路:

代码与最大深度类似,但是有个误区,如果出现了左子树或者右子树一边为null的情况,最终结果就为1了,但是实际上这并不是从根节点到最近叶子节点的最短路径上的节点数量,因此需要额外的做出判断

代码:

public int minDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    
    // 递归计算左子树的最小深度
    int leftDepth = minDepth(root.left);
    
    // 递归计算右子树的最小深度
    int rightDepth = minDepth(root.right);
    
    // 如果当前节点的左子树为空,右子树不为空,则最小深度为右子树的深度 + 1
    if (root.left == null && root.right != null) {
        return 1 + rightDepth;
    }
    
    // 如果当前节点的右子树为空,左子树不为空,则最小深度为左子树的深度 + 1
    if (root.left != null && root.right == null) {
        return 1 + leftDepth;
    }
    
    // 否则,当前节点的最小深度为左右子树最小深度的较小值 + 1
    return 1 + Math.min(leftDepth, rightDepth);
}
  1. 空树情况

    • 如果 root 为 null,即空树,则最小深度为 0,因此直接返回 0。
  2. 非空树情况

    • 首先递归计算左子树的最小深度 leftDepth
    • 然后递归计算右子树的最小深度 rightDepth
  3. 叶子节点情况

    • 如果当前节点 root 的左子树为空但右子树不为空,则最小深度为右子树的深度 + 1。这是因为要考虑到最小深度是从根节点到叶子节点的路径,所以只有右子树的情况需要特别处理。
    • 同理,如果当前节点的右子树为空但左子树不为空,则最小深度为左子树的深度 + 1。
  4. 一般情况

    • 如果当前节点既有左子树又有右子树,则最小深度为左右子树最小深度的较小值 + 1

 

三、完全二叉树节点的数量 

题目:

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例 1:

输入:root = [1,2,3,4,5,6]
输出:6

示例 2:

输入:root = []
输出:0

示例 3:

输入:root = [1]
输出:1

方法有很多

1、层序遍历

public int countNodes(TreeNode root) {
    if (root == null) {
        return 0;
    }
    
    // 递归计算左子树中节点的总数
    int leftNum = countNodes(root.left);
    
    // 递归计算右子树中节点的总数
    int rightNum = countNodes(root.right);
    
    // 当前节点(root)本身加上左右子树的节点数之和
    return 1 + leftNum + rightNum;
}

时间复杂度为O(n) 

2、利用完全二叉树的性质

思路:

由于完全二叉树不存在只有右子树而没有左子树的情况,因此我们可以通过判断左右子树最左边的节点和最右边节点的深度差来判断该二叉树中节点的数量,这样很大一部分中间的节点就没必要再次遍历,时间复杂度自然就小于O(n)了

代码:

public int countNodes(TreeNode root) {
    if (root == null) {
        return 0;
    }
    
    // 定义变量保存左子树和右子树
    TreeNode left = root.left;
    TreeNode right = root.right;
    int leftCount = 0, rightCount = 0;
    
    // 计算左子树的高度(即左子树的叶子节点数)
    while (left != null) {
        left = left.left;
        leftCount++;
    }
    
    // 计算右子树的高度(即右子树的叶子节点数)
    while (right != null) {
        right = right.right;
        rightCount++;
    }
    
    // 如果左子树的叶子节点数等于右子树的叶子节点数,则说明是满二叉树
    if (leftCount == rightCount) {
        // 计算满二叉树的节点总数并返回
        return (2 << leftCount) - 1;
    }
    
    // 如果不是满二叉树,则递归计算左右子树的节点总数,并加上当前节点
    int leftNum = countNodes(root.left);
    int rightNum = countNodes(root.right);
    return 1 + leftNum + rightNum;
}
  1. 空树情况

    • 如果 root 为 null,即空树,则节点总数为 0,直接返回 0。
  2. 非空树情况

    • 首先定义 left 和 right 变量分别指向根节点的左子树和右子树。
    • 使用 leftCount 和 rightCount 分别记录左子树和右子树的叶子节点数(即高度)。
    • 这里通过 while 循环找到左子树和右子树的最深的叶子节点,从而得到左右子树的高度。
  3. 判断满二叉树

    • 如果左子树的叶子节点数 leftCount 等于右子树的叶子节点数 rightCount,则说明当前子树是满二叉树。
    • 满二叉树的节点总数可以通过公式 (2 << leftCount) - 1 计算得到,即 2^leftCount - 1
  4. 非满二叉树情况

    • 如果左右子树的叶子节点数不相等,则该二叉树不是满二叉树。
    • 需要递归计算左右子树的节点总数,并将当前节点加上去(即加 1)。
  5. 递归调用

    • 如果不是满二叉树,则递归计算左子树和右子树的节点总数,分别存储在 leftNum 和 rightNum 中。
    • 最终返回的节点总数是当前节点 root 本身加上左右子树的节点总数。

3、迭代法 (BFS)

思路:

利用队列存储完全二叉树的所有节点,最后节点总数

代码:

public int countNodes(TreeNode root) {
    if (root == null) return 0; // 如果根节点为空,直接返回节点总数为0
    
    Queue<TreeNode> queue = new LinkedList<>(); // 创建一个队列用于BFS
    queue.offer(root); // 将根节点加入队列
    int result = 0; // 初始化节点总数为0
    
    while (!queue.isEmpty()) { // 当队列不为空时循环
        int size = queue.size(); // 获取当前队列的大小,即当前层的节点数
        while (size-- > 0) { // 遍历当前层的所有节点
            TreeNode cur = queue.poll(); // 出队一个节点
            result++; // 节点总数加1,表示访问了一个节点
            
            // 将当前节点的左右子节点加入队列(如果存在)
            if (cur.left != null) queue.offer(cur.left);
            if (cur.right != null) queue.offer(cur.right);
        }
    }
    
    return result; // 返回节点总数
}
  1. 初始条件检查

    • 首先检查根节点 root 是否为空。若为空,则整棵树没有节点,直接返回节点总数为0。
  2. 队列初始化

    • 创建一个 Queue<TreeNode> 类型的队列 queue,使用 LinkedList 实现。
  3. BFS遍历

    • 将根节点 root 加入队列 queue
    • 初始化节点总数 result 为0,用于统计访问过的节点数。
  4. 主循环

    • 使用 while (!queue.isEmpty()) 进行 BFS 的迭代,直到队列为空。
    • 在每次迭代中,获取当前队列的大小 size,表示当前层的节点数。
  5. 处理当前层节点

    • 通过 size-- > 0 的循环方式遍历当前层的所有节点:
      • 从队列中取出一个节点 cur
      • 将 result 增加1,表示访问了一个节点。
  6. 扩展下一层

    • 检查当前节点 cur 的左右子节点是否存在,如果存在则加入队列 queue 中,以便在下一次迭代时处理。

 

四、平衡二叉树 

题目:

给定一个二叉树,判断它是否是 

平衡二叉树

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:true

示例 2:

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

示例 3:

输入:root = []
输出:true

思路:

是否平衡的前提是要判断左右子树的高度差是否平衡,采用后序遍历的方法,从叶子节点逐层向上排查左右子树高度差,若高度差大于1则不是平衡二叉树

代码:

递归计算每个节点的高度,并判断是否平衡

public int getHeight(TreeNode root) {
    if (root == null) {
        return 0; // 空节点的高度为0
    }
    int leftHeight = getHeight(root.left); // 获取左子树的高度
    int rightHeight = getHeight(root.right); // 获取右子树的高度
    
    // 如果左子树或右子树不是平衡的,则整棵树也不平衡,返回-1
    if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
        return -1;
    }
    
    // 返回当前节点为根的子树的高度
    return Math.max(leftHeight, rightHeight) + 1;
}
  • 如果 root 为 null,则返回高度 0
  • 分别计算左子树和右子树的高度 leftHeight 和 rightHeight
  • 如果左子树或右子树的高度为 -1,或者左右子树的高度差大于 1,则返回 -1,表示当前子树不是平衡的。
  • 否则,返回当前节点为根的子树的高度,即 Math.max(leftHeight, rightHeight) + 1
//调用 getHeight(root) 方法,如果返回的高度不等于 -1,则说明是平衡二叉树;否则,不是平衡二叉树。
public boolean isBalanced(TreeNode root) {
    return getHeight(root) != -1;
}

这种方法的时间复杂度为 O(n),其中 n 是二叉树的节点数,因为每个节点都需要计算其高度一次。

今天的学习就到这里 

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值