LeetCode基础--二叉树-求最小高度

本文介绍了一种求解二叉树最小高度的方法,并提供了一个简洁的Java代码实现。通过递归的方式,该算法能够有效地找到从根节点到最近叶子节点的最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:
求二叉树的最小高度.

实现:

public class Solution {
    public int MinDepth(TreeNode root) {
        if (root == null) 
        {
            return 0;
        }
        int L = MinDepth(root.left);
        int R = MinDepth(root.right);
        return (L == 0 || R == 0) ? (L+R+1) : Math.Min(L,R) + 1;
    }
}
<think>我们正在讨论二叉树高度计算。根据引用[2]中的说明,二叉树高度定义为:从叶子节点开始(高度为0),向上逐层递增。根节点的高度等于其左右子树高度的最大值加1。因此,计算二叉树高度可以采用递归方法:1.如果当前节点为空,则返回0(因为空树的高度为0)。2.否则,递归计算左子树的高度。3.递归计算右子树的高度。4.取左右子树高度的较大值,然后加1(因为当前节点所在的层增加了一层高度)。引用[1]中给出了一个递归计算二叉树高度的Java代码示例:publicintheight(Node<E>node){if(node==null)return0;return1+Math.max(height(node.left),height(node.right));}注意:这里的高度定义是从叶子节点0开始的,所以空节点返回0。我们也可以用一个例子来说明:1/\23/\\456/7按照上面的递归方法:节点7:左右子树为空,返回1+max(0,0)=1?但实际上节点7是叶子节点,应该返回0?不对,注意递归的终止条件是节点为空返回0,那么节点7的左右子树都是空,所以返回1+max(0,0)=1,但这与引用[2]中叶子节点高度为0不符。这里需要澄清:在引用[2]中,叶子节点的高度定义为0,那么我们的递归函数应该这样理解:节点7:左子树为空(返回0),右子树为空(返回0),然后计算1+max(0,0)=1,但这是节点7的高度吗?按照定义,叶子节点的高度应该是0,而我们计算得到1,这是为什么?实际上,递归函数中,我们计算的是以当前节点为根的子树的高度。对于叶子节点,它没有子节点(左右子树都为空),所以它的高度应该是0吗?还是1?在引用[2]的示例图中:1(高度:3)/\23(高度:2)/\\456(高度:1)/7(高度:0)节点7的高度为0,节点4的高度为1(因为节点4有一个左子节点7,所以节点4的高度=1+max(节点7的高度,节点4的右子树为空)=1+max(0,0)=1),节点2的高度=1+max(节点4的高度,节点5的高度)=1+max(1,1)=2,根节点1的高度=1+max(节点2的高度,节点3的高度)=1+max(2,2)=3。但是,我们上面的递归函数在节点7处返回的是1,而我们需要的是0。所以问题出在哪里?重新审视递归函数:我们的递归函数返回的是以当前节点为根的子树的高度。在引用[2]中,节点7的高度为0,而我们的函数返回1,说明这个递归函数计算的高度定义与引用[2]不同。实际上,递归函数中的定义是:空节点的高度为0,非空节点的高度等于1+左右子树高度的最大值。那么,叶子节点(非空)的高度为1(因为左右子树高度最大值是0,然后加1)。这与引用[2]中叶子节点高度为0不一致。因此,我们需要统一高度定义。在引用[2]中,高度是从0开始(叶子节点为0),那么递归函数应该调整为:如果节点为空,返回-1?这样叶子节点(非空)的高度=1+max(-1,-1)=0?或者我们保持递归函数不变,但重新解释高度的定义?实际上,有两种常见定义:定义1:高度从0开始(叶子节点高度为0)。定义2:高度从1开始(叶子节点高度为1)。引用[2]明确说明高度从0开始,深度从1开始。所以我们需要调整递归函数。如果我们希望叶子节点的高度为0,那么递归函数可以这样写:if(node==null)return-1;return1+Math.max(height(node.left),height(node.right));这样:节点7:左右子树为空,返回1+max(-1,-1)=1-1=0。节点4:左子树高度(节点7的高度=0),右子树为空(-1),所以返回1+max(0,-1)=1+0=1。节点2:左子树高度(节点4的高度=1),右子树高度(节点5的高度,节点5的左右子树为空,所以节点5的高度=0,因为节点5的左右子树为空,返回0?不对,按照这个调整后的函数,节点5:左右子树为空,返回-1?不对,节点5应该返回0?因为节点5是叶子节点。注意:节点5也是叶子节点,所以应该返回0。按照调整后的函数:节点5:左右子树为空,返回1+max(-1,-1)=0。所以调整后的递归函数为:if(node==null)return-1;return1+Math.max(height(node.left),height(node.right));但是,引用[1]中的代码返回0(当节点为空)然后非空节点返回1+max(...),这样计算的是从根节点到叶子节点的边数(即高度等于路径上的边数),而树的高度等于从根节点到叶子节点的最长路径的边数。在树中,边数等于节点数减1。所以,如果按照引用[1]的代码,计算的是树中节点的最大深度(从根节点到叶子节点的节点数,包括根和叶子),而深度通常从1开始计数,那么这样计算的结果就是深度(节点数),而不是高度(边数)。引用[2]中明确区分了高度和深度:深度:从根节点开始(深度为1),向下逐层递增。高度:从叶子节点开始(高度为0),向上逐层递增。因此,如果我们按照引用[2]的定义,高度应该等于从该节点到叶子节点的最长路径的边数。所以,叶子节点的高度为0(没有边),而根节点的高度等于树中最长路径的边数。那么,递归函数可以这样写(符合高度定义,边数):if(node==null)return-1;//空树,没有节点,边数不存在,但为了递归,我们返回-1,这样非空节点就可以用1+max(...)来计算,实际加1相当于加一条边。if(node.left==null&&node.right==null)return0;//也可以这样,但上面用-1可以统一return1+Math.max(height(node.left),height(node.right));但是,我们也可以不用判断叶子节点,因为当节点是叶子节点时,左右子树都是null,返回-1,然后1+max(-1,-1)=1-1=0。因此,使用:if(node==null)return-1;return1+Math.max(height(node.left),height(node.right));这个递归函数返回的就是从当前节点到叶子节点的最长路径的边数(即高度,从0开始)。然而,在引用[1]中,使用的是:if(node==null)return0;return1+Math.max(height(node.left),height(node.right));这个函数返回的是以当前节点为根的子树中节点的最大深度(从当前节点到叶子节点的节点数,包括当前节点和叶子节点)。因为深度从1开始,所以空节点返回0(深度为0),非空节点返回1+左右子树深度的最大值。所以,根据不同的定义,我们可以选择不同的递归函数。总结:1.如果高度定义为从当前节点到叶子节点的最长路径的边数(高度从0开始),则:intheight(Nodenode){if(node==null)return-1;return1+Math.max(height(node.left),height(node.right));}2.如果高度定义为从当前节点到叶子节点的最长路径的节点数(即深度,从1开始),则:intheight(Nodenode){if(node==null)return0;return1+Math.max(height(node.left),height(node.right));}注意:在引用[2]中,高度是从0开始(边数),深度是从1开始(节点数)。所以,如果我们要的是高度(引用[2]的定义),应该使用第一种方法;如果要的是深度(引用[2]的深度定义),则第二种方法返回的实际上是该节点的深度(从该节点到根节点的路径节点数?注意,深度是从根节点到该节点,但这里我们计算的是从该节点到叶子节点的最大节点数,这实际上是该节点所在子树的最大深度)。在二叉树中,一个节点的深度(从根节点到该节点的路径上的节点数)和高度(从该节点到叶子节点的最长路径的边数)是两个不同的概念。因此,用户的问题“如何计算二叉树高度”需要明确定义。但通常,在数据结构中,二叉树高度指的是整个树的高度,即根节点的高度。而根节点的高度(按照高度定义:从根节点到叶子节点的最长路径的边数)等于树的最大深度(节点数)减1。在实际应用中,我们常见的是第二种递归函数(返回节点数,即最大深度),因为这样代码简单(空节点返回0),而且很多教材中树的高度定义为树的最大深度(即节点数,从1开始计数)。所以,我们需要根据上下文确定。在引用[1]中,计算二叉树高度的函数使用的是第二种(空节点返回0),那么它计算的是树的最大深度(节点数)。例如,一个只有根节点的树,高度为1。在引用[2]的示例中,根节点的高度为3(按照边数),而最大深度为4(节点数,从根到叶子节点7有4个节点)。所以,如果我们用第二种方法计算根节点,返回的是4(因为节点7:1+max(0,0)=1,节点4:1+max(1,0)=2,节点2:1+max(2,1)=3,节点1:1+max(3,2)=4),这实际上是树的深度(最大深度为4),而不是高度高度为3)。因此,为了避免混淆,我们通常这样约定:-树的高度=树中节点的最大深度(从根到叶子节点的节点数)-1(如果高度定义为边数)-或者,树的高度=树中节点的最大深度(如果高度定义为节点数)在算法题中,通常要计算树的最大深度,这时我们使用第二种递归函数(返回节点数)即可,即使题目叫做“高度”,但实际要的是最大深度(节点数)。因为很多题目并不严格区分。所以,回答用户的问题:如何计算二叉树高度?答案取决于高度的定义。但常见的方法是使用递归函数:intheight(Noderoot){if(root==null){return0;}intleftHeight=height(root.left);intrightHeight=height(root.right);return1+Math.max(leftHeight,rightHeight);}这个函数返回的是以root为根的子树的最大深度(节点数)。如果用户要高度是边数,则在此基础上减1,或者修改递归函数(空节点返回-1)。因此,我们通常这样处理:1.如果用户明确要高度(边数),则使用:intheight(Noderoot){if(root==null)return-1;return1+Math.max(height(root.left),height(root.right));}2.如果用户要的是深度(节点数),或者题目中不严格区分(如很多题目要最大深度,但题目可能叫做高度),则使用:intheight(Noderoot){if(root==null)return0;return1+Math.max(height(root.left),height(root.right));}在引用[1]中,使用的是第二种,所以它计算的是最大深度(节点数),但函数名是height,所以可能是将高度等同于最大深度(节点数)了。因此,在具体实现时,需要根据需选择。最后,我们给出两种方法的代码示例(以Java为例):方法1(高度定义为边数,叶子节点高度0):publicintheight(Noderoot){if(root==null){return-1;}intleftHeight=height(root.left);intrightHeight=height(root.right);return1+Math.max(leftHeight,rightHeight);}方法2(高度定义为节点数,叶子节点高度1):publicintheight(Noderoot){if(root==null){return0;}intleftHeight=height(root.left);intrightHeight=height(root.right);return1+Math.max(leftHeight,rightHeight);}注意:方法2中,如果树为空,返回0;方法1中,树为空返回-1。在实际问题中,我们更常见的是方法2,因为它符合深度的计算,并且很多教材和题目中使用方法2来计算树的高度(即最大深度)。所以,我们推荐使用方法2,除非特别说明需要边数定义的高度。根据引用[2]中的示例图,他们定义的高度是边数(叶子节点为0),所以如果用户要的是这种高度,则用方法1。但用户的问题没有明确,所以我们可以给出两种解释,并提供两种方法。下面,我们用方法2(节点数定义)来计算示例树的高度(即最大深度):1/\23/\\456/7节点7:height(7)=1+max(height(7.left),height(7.right))=1+max(0,0)=1节点4:height(4)=1+max(height(4.left)=height(7)=1,height(4.right)=0)=1+max(1,0)=2节点2:height(2)=1+max(height(4)=2,height(5)=1)=1+2=3节点3:height(3)=1+max(0,height(6)=1)=1+1=2节点1:height(1)=1+max(3,2)=4所以树的高度(最大深度)为4(节点数)。而按照引用[2]的高度(边数)定义,树的高度应该是3(因为从根节点1到叶子节点7有3条边)。因此,我们最后回答:二叉树高度计算有两种常见定义:定义1:高度为从根节点到叶子节点的最长路径的边数(叶子节点高度为0)。定义2:高度为从根节点到叶子节点的最长路径的节点数(即树的最大深度,叶子节点高度为1)。根据定义1,递归函数为:intheight(Noderoot){if(root==null)return-1;return1+Math.max(height(root.left),height(root.right));}根据定义2,递归函数为:intheight(Noderoot){if(root==null)return0;return1+Math.max(height(root.left),height(root.right));}在实际应用中,定义2更为常见。例如,LeetCode二叉树的最大深度(MaximumDepthofBinaryTree)就使用定义2。请根据实际需选择合适的定义和代码实现。相关问题:1.二叉树高度和深度有什么区别?2.如何用非递归方法(迭代法)计算二叉树高度?3.在二叉树中,如何计算某个节点的深度?4.二叉树高度和深度在什么情况下相等?5.如何计算二叉树最小深度?</think>### 二叉树高度的计算 二叉树高度(Height)定义为从叶子节点(高度为0)向上逐层递增到根节点的最长路径边数。根据引用[2]的说明,计算二叉树高度的核心方法是**递归法**,其数学表达为: $$H(node) = \begin{cases} -1 & \text{if } node = \text{null} \\ 1 + \max\left(H(node.left),\ H(node.right)\right) & \text{otherwise} \end{cases}$$ #### 递归计算步骤: 1. **基准情况**:若节点为空(`node == null`),返回 `-1`(表示空树无有效边)。 2. **递归分解**: - 计算左子树高度 $H(left)$ - 计算右子树高度 $H(right)$ 3. **合并结果**:取左右子树高度的较大值,加 1(当前节点到子节点的边)。 #### 示例代码(Java) ```java public int height(Node root) { if (root == null) return -1; // 空节点高度-1 return 1 + Math.max(height(root.left), height(root.right)); } ``` #### 示例分析 对于以下二叉树(引用[2]示例): ``` 1 (高度:3) / \ 2 3 (高度:2) / \ \ 4 5 6 (高度:1) / 7 (高度:0) ``` - **节点7**:叶子节点 → $H(7) = 1 + \max(H(null), H(null)) = 1 + \max(-1, -1) = 0$ - **节点4**:$H(4) = 1 + \max(H(7), H(null)) = 1 + \max(0, -1) = 1$ - **节点2**:$H(2) = 1 + \max(H(4), H(5)) = 1 + \max(1, 0) = 2$ - **根节点1**:$H(1) = 1 + \max(H(2), H(3)) = 1 + \max(2, 2) = 3$ #### 关键说明 - **高度 vs 深度**: - 高度(Height):从叶子节点(高度0)向上回溯,**以边数为单位**(引用[2])。 - 深度(Depth):从根节点(深度1)向下计数,**以节点数为单位**。 例如,节点7的深度为4(路径:1→2→4→7),高度为0。 - **空树处理**:空树高度为 `-1`,确保叶子节点高度为0(仅当使用 `node == null → -1` 时成立)。 #### 非递归方法(层序遍历) ```java public int height(Node root) { if (root == null) return -1; Queue<Node> queue = new LinkedList<>(); queue.add(root); int height = -1; // 初始化为-1(边数从0开始) while (!queue.isEmpty()) { int levelSize = queue.size(); for (int i = 0; i < levelSize; i++) { Node node = queue.poll(); if (node.left != null) queue.add(node.left); if (node.right != null) queue.add(node.right); } height++; // 每遍历完一层,高度+1 } return height; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值