【力扣一刷】代码随想录day16 (104.二叉树的最大深度、559.n叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数)【递归版】

目录

【基础知识】

【104.二叉树的最大深度】

方法一  间接法【求高度,递归 - 后序遍历】

方法二  直接法【求深度,递归 - 前序遍历】

【559.n叉树的最大深度】

方法一  间接法【求高度,递归 - 后序遍历】

方法二  直接法【求深度,递归 - 前序遍历】

【111.二叉树的最小深度】

方法一  递归 - 前序遍历

方法二  递归 - 后序遍历

【222.完全二叉树的节点个数】

方法一  递归 - 按照普通二叉树的规则

方法二  递归 - 针对完全二叉树的性质


【基础知识】

二叉树的高度和深度

  • 高度:当前节点距离叶子节点的距离,通常用后序遍历求高度。
  • 深度:当前节点距离根节点的距离,通常用前序遍历求深度。


【104.二叉树的最大深度】

方法一  间接法【求高度,递归 - 后序遍历】

关键:

  • 二叉树的最大深度 = 根节点的最大高度
  • 父节点的最大高度 = 左右子节点高度的最大值 + 1(需要先访问左右节点,再访问中节点,所以对应的是后序遍历)

思路:

  • 确定参数和返回值:目的是获取当前节点的高度,输入的是TreeNode类型的节点,输出int型高度
  • 确定终止条件:如果当前节点为null,则返回高度为0(注意:叶子节点的高度为1)
  • 确定单层递归逻辑:获取当前节点/父节点的高度,即要获取左右子节点的最大高度再+1
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        // 关键:最大深度 = 根节点的高度,求高度用后序遍历
        // 1、确定参数和返回值:目的是获取当前节点的高度,输入的是TreeNode类型的节点,输出int型高度
        // 2、确定终止条件:如果当前节点为null,则返回高度为0(注意:叶子节点的高度为1)
        if (root == null) return 0;
        // 3、确定单层递归逻辑:获取当前节点/父节点的高度,即要获取左右子节点的最大高度再+1
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

方法二  直接法【求深度,递归 - 前序遍历】

关键:

直接求深度,用前序遍历,子节点的深度 = 父节点的深度 + 1

思路:

1、确定参数和返回值:目的是计算当前节点的深度和当前的最大深度,因此需要传入当前节点和父节点的深度。由于是前序遍历,先获取的当前节点/中节点的深度,再获取左右节点的深度,此时左右节点的深度返回没有意义。因此,参数为TreeNode类的当前节点,int型的父节点深度,返回值为void。

 2、确定终止条件:当前节点为null时,计算深度没有意义,直接返回即可

 3、确定单层递归逻辑

  • 计算中节点/当前节点的最大深度
  • 将中节点的深度与当前最大深度比较,更新最大深度
  • 计算左节点的深度
  • 计算右节点的深度
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int depth = 0; // 需要定义为成员变量,便于在递归过程中更新最大深度
    public int maxDepth(TreeNode root) {
        getDepth(root, 0);  // 对于根节点,父节点的深度为0,根节点的深度为1
        return depth;
    }

    // 1、确定参数和返回值:目的是计算当前节点的深度和当前的最大深度,因此需要传入当前节点和父节点的深度
    // 由于是前序遍历,先获取的当前节点/中节点的深度,再获取左右节点的深度,此时左右节点的深度返回没有意义
    // 因此,参数为TreeNode类的当前节点,int型的父节点深度,返回值为void
    public void getDepth(TreeNode root, int fatherDepth){
        // 2、确定终止条件:当前节点为null时,计算深度没有意义,直接返回即可
        if (root == null) return;

        // 3、确定单层递归逻辑:计算中节点的深度,将中节点的深度与当前最大深度比较,更新最大深度,再计算左右节点的深度
        // 计算中节点/当前节点的最大深度
        int nowDepth = fatherDepth + 1;
        depth = Math.max(depth, nowDepth);

        // 获取左节点的深度
        getDepth(root.left, nowDepth);

        // 获取右节点的深度
        getDepth(root.right, nowDepth);

    }
}


【559.n叉树的最大深度】

方法一  间接法【求高度,递归 - 后序遍历】

思路和104的方法一相同

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    public int maxDepth(Node root) {
        // 最大深度 = 根节点的高度,高度用递归的后序遍历,父节点的高度 = 左右子节点的高度最大值 + 1
        // 1、确定参数和返回值:输入节点(Node),获取节点的深度(int)
        // 2、确定终止条件:如果当前节点为null,高度为0
        if (root == null) return 0;

        // 3、确定单层逻辑:获取所有子节点的高度的最大值,再+1作为当前节点的高度
        int maxHeight = 0;
        for (int i = 0; i < root.children.size(); i++){
            maxHeight = Math.max(maxHeight, maxDepth(root.children.get(i)));
        }
        return maxHeight + 1;
    }
}

方法二  直接法【求深度,递归 - 前序遍历】

思路和104的方法一相同

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    int maxDepth = 0;
    public int maxDepth(Node root) {
        // 直接求最大深度,利用前序遍历(中左右)求,子节点的深度 = 父节点的深度 + 1
        getMaxDepth(root, 0);
        return maxDepth;
    }
    // 1、确定参数和返回值
    public void getMaxDepth(Node root, int fatherDepth){
        // 2、确定终止条件
        if (root == null) return;

        // 3、确定单层递归逻辑
        int depth = fatherDepth + 1;
        maxDepth = Math.max(maxDepth, depth);
        for (int i = 0; i < root.children.size(); i++){
            getMaxDepth(root.children.get(i), depth);
        }
    }


}


【111.二叉树的最小深度】

方法一  递归 - 前序遍历

思路:

  • 最小深度:所有叶子节点的深度的最小值
  • 如果当前节点是叶子节点,才有机会更新最小深度。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int minDepth = Integer.MAX_VALUE;
    public int minDepth(TreeNode root) {
        if (root == null) return 0;
        getMinDepth(root, 1);
        return minDepth;
    }

    // 1、确定参数值,传入当前节点root和当前节点的深度depth,不需要返回值,更新成员变量minDepth即可
    public void getMinDepth(TreeNode root, int depth){
        // 2、确定终止条件
        if (root == null) return;

        // 3、确定单层递归逻辑
        // 只有当一个节点没有左右子节点时,才是叶子节点,才需要将当前叶子节点的深度与当前最小深度比较
        if (root.left == null && root.right == null){
            minDepth = Math.min(minDepth, depth);
        }
        
        // 遍历左节点和右节点
        getMinDepth(root.left, depth + 1);
        getMinDepth(root.right, depth + 1);
    }
}

方法二  递归 - 后序遍历

思路:

  • 最小深度 = 根节点的最小高度
  • 父节点的最小高度 = 左右节点的高度最小值 + 1

注意:

如果左右节点其中一个为null,则不考虑null子节点的高度。例如:

如果考虑null左节点,那么根节点的最小高度就是1,明显是错误的。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 最小深度 = 根节点的最小高度,后序遍历求高度,父节点的最小高度 = 左右节点高度的最小值 + 1
    // 1、确定参数和返回值,参数就是传入当前节点,返回值就是int型的当前节点的最小高度
    public int minDepth(TreeNode root) {
        
        // 2、确定终止条件
        if (root == null) return 0;

        // 3、确定单层递归循环
        // 如果左节点为空,则只考虑右节点的高度
        if (root.left == null) return minDepth(root.right) + 1;
        // 如果右节点为空,则只考虑左节点的高度
        if (root.right == null) return minDepth(root.left) + 1;
        // 如果左右节点都不为空,则取左右节点高度的最小值 + 1 作为当前节点的最小高度
        return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
    }
}


【222.完全二叉树的节点个数】

方法一  递归 - 按照普通二叉树的规则

思路1:递归遍历所有节点,每遍历到一个节点就使num+1

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int num = 0;
    public int countNodes(TreeNode root) {
        getNum(root);
        return num;
    }

    // 1、确定参数和返回值:传入当前节点,不需要返回,在遍历到当前节点的时候num++即可
    public void getNum(TreeNode root){
        // 2、确定终止条件
        if (root == null) return;

        // 3、确定单层递归逻辑
        num++;
        getNum(root.left);
        getNum(root.right);
    }
}

思路2:当前节点对应子树的所有节点数 = 左右节点对应子树的所有节点数之和 + 1

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 1、确定参数和返回值,该函数的作用是返回当前节点对应子树的所有节点数量
    public int countNodes(TreeNode root) {
        // 2、确定终止条件
        if (root == null) return 0;

        // 3、确定单层递归逻辑
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}

两种思想的复杂度一致:

时间复杂度:O(n) ,有可能是满二叉树,相当于遍历一次所有节点

空间复杂度:O(n),取决于递归的次数,即相当于遍历一次所有节点

方法二  递归 - 针对完全二叉树的性质

完全二叉树:从上到下,从左到右,节点都是不间断的,而且只有最后一层缺少节点的树。

思路:判断该节点对应的子树是否为满二叉树

判断方法:只向左遍历的深度 = 只向右遍历的深度,则该树为满二叉树

  • 如果是,则根据2^k - 1计算该节点对应子树的所有节点数
  • 如果不是,继续遍历左节点和右节点,当前节点对应子树的所有节点数 = 左右子树所有节点之和 + 1
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 判断该节点对应的树是否为满二叉树,是的话就返回该二叉树的深度,否的话就返回-1
    public int isFull(TreeNode root){
        // 计算只向左遍历的深度
        TreeNode tmp = root;
        int leftDepth = 0;
        while(tmp != null){
            leftDepth++;
            tmp = tmp.left;
        }

        // 计算只向右遍历的深度
        tmp = root;
        int rightDepth = 0;
        while(tmp != null){
            rightDepth++;
            tmp = tmp.right;
        }

        return leftDepth == rightDepth ? leftDepth : -1;
    }

    // 1、确定参数和返回值,该函数的作用是返回当前节点对应子树的所有节点数量
    public int countNodes(TreeNode root) {
        // 2、确定终止条件
        if (root == null) return 0;

        // 3、确定单层递归逻辑
        // 判断该节点对应的子树是否为满二叉树,
        int k = isFull(root);
        // - 如果是,则根据2^k - 1计算该节点对应子树的所有节点数
        if (k != -1){
            return (int) Math.pow(2, k) - 1;  // 注意Math.pow返回的是double型数据,要进行强转才能-1
        }
        // - 如果不是,继续遍历左节点和右节点
        else{
            int leftNum = countNodes(root.left);
            int rightNum = countNodes(root.right);
            return leftNum + rightNum + 1;
        }
    }
}

时间复杂度:O(logn × logn) ,判断O(logn),访问左右子树也是O(logn),访问后要判断即要相乘

空间复杂度:O(logn),取决于递归的深度,完全二叉树的递归深度为 O(log n)

  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值