【算法训练-二叉树 三】【最大深度与直径】求二叉树的最大深度、求二叉树的直径

2023-09-20 23:32:09

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【求二叉树的直径】,使用【二叉树】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为:目标公司+最近一年+出现频率排序,由高到低的去牛客TOP101去找,只有两个地方都出现过才做这道题(CodeTop本身汇聚了LeetCode的来源),确保刷的题都是高频要面试考的题。

名曲目标题后,附上题目链接,后期可以依据解题思路反复快速练习,题目按照题干的基本数据结构分类,且每个分类的第一篇必定是对基础数据结构的介绍

求二叉树的最大深度【EASY】

求二叉树的最大深度

题干

直接粘题干和用例

解题思路

最大深度是所有叶子节点的深度的最大值,深度是指树的根节点到任一叶子节点路径上节点的数量,因此从根节点每次往下一层深度就会加1。因此二叉树的深度就等于根节点这个1层加上左子树和右子树深度的最大值

  1. 终止条件: 当进入叶子节点后,再进入子节点,即为空,没有深度可言,返回0.
  2. 返回值: 每一级按照上述公式,返回两边子树深度的最大值加上本级的深度,即加1.
  3. 本级任务: 每一级的任务就是进入左右子树,求左右子树的深度。

在这里插入图片描述

代码实现

给出代码实现基本档案

基本数据结构二叉树
辅助数据结构
算法递归、DFS
技巧

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {

    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param root TreeNode类
     * @return int整型
     */
    public int maxDepth (TreeNode root) {
        // 1 如果只有根节点,返回1
        if (root == null) {
            return 0;
        }
        // 2 递归获取左子树最大深度
        int leftMaxLenth = maxDepth(root.left);
        // 3 递归获取右子树最大深度
        int rightMaxLenth = maxDepth(root.right);
        // 4 返回当前最大深度
        return Math.max(leftMaxLenth, rightMaxLenth) + 1;
    }
}

复杂度分析

时间复杂度:O(n),其中n为二叉树的节点数,遍历整棵二叉树
空间复杂度:O(n),最坏情况下,二叉树化为链表,递归栈深度最大为n

求二叉树的直径【EASY】

相对于求深度难度有所升级。

题干

直接粘题干和用例

解题思路

依据题意可以得出,直径最大应该是两个叶子节点之间的路径。首先我们知道一条路径的长度为该路径经过的节点数减一,所以求直径(即求路径长度的最大值)等效于求路径经过节点数的最大值减一任意一条路径均可以被看作由某个节点为起点,从其左儿子和右儿子向下遍历的路径拼接得到,也就是其两边子树最大深度之和,但需要注意的是,这个节点不一定是根节点,只是直径路径上两个节点的公共节点而已

所以问题就转换成了求两个叶子节点之间最大距离的公共节点

在这里插入图片描述

代码实现

给出代码实现基本档案

基本数据结构二叉树
辅助数据结构
算法迭代
技巧

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

 private int maxNodeNum;
    public int diameterOfBinaryTree(TreeNode root) {
        // 1 处理异常情况
        if (root == null) {
            return 0;
        }
        // 2 初始途径节点设置为1
        maxNodeNum = 1;
        // 3 递归获取根节点最大深度,过程中求最大直径
        maxDepth(root);
        // 4 全部途径节点数-1为最终结果直径
        return maxNodeNum - 1;
    }

    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param root TreeNode类
     * @return int整型
     */
    public int maxDepth (TreeNode root) {
        // 1 如果只有根节点,返回1
        if (root == null) {
            return 0;
        }
        // 2 递归获取左子树最大深度
        int leftMaxLenth = maxDepth(root.left);
        // 3 递归获取右子树最大深度
        int rightMaxLenth = maxDepth(root.right);
        // 4 直径为左右最大深度和+1(要算上根节点)
        int curNodeNum = leftMaxLenth + rightMaxLenth + 1;
        maxNodeNum = Math.max(maxNodeNum, curNodeNum);
        return Math.max(leftMaxLenth, rightMaxLenth) + 1;
    }

复杂度分析

时间复杂度:遍历了整棵树节点,时间复杂度为O(N)
空间复杂度:极端情况下,二叉树退化为链表,递归栈的深度为O(N),空间复杂度为O(N)

拓展知识:二叉树的最大深度与直径

二叉树的直径和最大深度是树结构中两个不同但相关的概念。

  1. 最大深度(Maximum Depth):
    最大深度是指二叉树中从根节点到叶子节点的最长路径的长度。通常,可以使用递归算法来计算最大深度,如下所示的伪代码:
function maxDepth(node):
    if node is null:
        return 0
    leftDepth = maxDepth(node.left)
    rightDepth = maxDepth(node.right)
    return max(leftDepth, rightDepth) + 1
  1. 二叉树的直径(Diameter of a Binary Tree):
    二叉树的直径是指二叉树中任意两个节点之间的最长路径的长度这个路径不一定通过根节点。计算二叉树的直径通常需要通过递归来查找,可以使用以下方法:
function diameterOfBinaryTree(root):
    if root is null:
        return 0
    
    # 计算左子树的最大深度
    leftDepth = maxDepth(root.left)
    
    # 计算右子树的最大深度
    rightDepth = maxDepth(root.right)
    
    # 计算经过根节点的直径
    rootDiameter = leftDepth + rightDepth
    
    # 计算左子树的直径
    leftDiameter = diameterOfBinaryTree(root.left)
    
    # 计算右子树的直径
    rightDiameter = diameterOfBinaryTree(root.right)
    
    # 返回三者中的最大值
    return max(rootDiameter, leftDiameter, rightDiameter)

请注意,这个算法的时间复杂度较高,因为它在每个节点上都会多次计算最大深度。如果需要优化性能,可以使用动态规划或记忆化搜索来避免重复计算。

更多推荐

网络安全进阶学习第二十课——CTF之文件操作与隐写

文章目录一、文件类型识别1、File命令2、Winhex3、文件头残缺/错误二、文件分离操作1、Binwalk工具2、Foremost3、dd4、Winhex三、文件合并操作1、Linux下的文件合并2、Windowsa下的文件合并四、文件内容隐写Winhex五、图片文件隐写1、图片混合2、LSB(最低有效位Least

PLC串口通讯和通讯接口知识汇总

在使用PLC的时候会接触到很多的通讯协议以及通讯接口,最基本的PLC串口通讯和基本的通讯接口你都了解吗?一、什么是串口通讯?串口是一种接口标准,是计算机上一种非常通用设备通信的协议。它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。典型的串口通讯标准常见有如下三种。EIARS232(通常简称“RS232”):

【C++】命名空间 namespace 与 标准流 iostream ( 命名空间概念简介 | 命名空间定义 | 命名空间使用 | iostream 中的命名空间分析 )

文章目录一、命名空间namespace1、命名空间基本概念2、名称概念4、C语言的命名空间3、命名空间避免标识符冲突二、命名空间定义1、命名空间基本概念2、命名空间定义语法3、代码示例-命名空间定义使用三、命名空间使用1、命名空间默认访问方式2、使用命名空间3、使用默认的命名空间4、代码示例-使用命名空间四、标准流io

MySQL学习系列(11)-每天学习10个知识

目录1.数据库设计的关键因素2.使用存储过程和函数来提高性能和可重用性3.MySQL性能优化4.使用视图简化查询和提供数据安全性5.数据库备份和恢复策略的重要性和实践经验6.在分布式系统中保证数据一致性和可用性7.理解MySQL的复制和其在实际应用中的作用8.使用游标进行分页查询和遍历查询结果9.使用窗口函数10.数据

Redis 面试常见问答

本文出自:https://thinkinjava.cn作者:莫那鲁道1.什么是缓存雪崩?怎么解决?一般而言,我们会利用缓存来缓冲对数据库的冲击,假如缓存无法正常工作,所有的请求便会直接发送至数据库,进而导致数据库崩溃,从而导致整个系统崩溃。如何解决呢?2种策略(同时使用):对缓存做高可用,防止缓存宕机使用断路器,如果缓

深入学习 Redis - 分布式锁底层实现原理,以及实际应用

目录一、Redis分布式锁1.1、什么是分布式锁1.2、分布式锁的基础实现1.2.1、引入场景1.2.2、基础实现思想1.2.3、引入setnx1.3、引入过期时间1.4、引入校验id1.5、引入lua脚本1.5.1、引入lua脚本的原因1.5.2、lua脚本介绍1.6、过期时间续约问题(看门狗WatchDog)1.7

十四、流式编程(2)

本章概要中间操作跟踪和调试流元素排序移除元素应用函数到元素在map()中组合流中间操作中间操作用于从一个流中获取对象,并将对象作为另一个流从后端输出,以连接到其他操作。跟踪和调试peek()操作的目的是帮助调试。它允许你无修改地查看流中的元素。代码示例:Peeking.javaclassPeeking{publicst

Docker概念通讲

目录什么是Docker?Docker的应用场景有哪些?Docker的优点有哪些?Docker与虚拟机的区别是什么?Docker的三大核心是什么?如何快速安装Docker?如何修改Docker的存储位置?Docker镜像常用管理有哪些?如何创建Docker容器?Docker在后台的标准运行过程是什么?Docker网络模式

Apollo

Apollo🍓目前市面上比较多的配置中心🍓Apollo组件🍓Apollo特性🍓Apollo服务端安装🍓部署架构🍓核心概念🍓客户端连接Apollo🍓配置发布原理代码地址:https://gitee.com/xuhx615/apollo-demo.git🍓目前市面上比较多的配置中心⭐Disconf百度开源

Dubbo3基础使用

1、Dubbo概述现在SpringCloudAlibaba比较火,用的比较多是吧,那dubbo是不是过时的呢?并不是的,以前有人把Dubbo和SpringCloud进行对比,其实两者是不同维度的,不能对比,dubbo就是一个rpc框架,SpringCloud是一个生态,里面包括很多组件,并且dubbo3也可以和Spri

【K8S系列】深入解析k8s网络插件—Weave Net

序言做一件事并不难,难的是在于坚持。坚持一下也不难,难的是坚持到底。文章标记颜色说明:黄色:重要标题红色:用来标记结论绿色:用来标记论点蓝色:用来标记论点Kubernetes(k8s)是一个容器编排平台,允许在容器中运行应用程序和服务。今天学习一下k8s网络插件-WeaveNet相关知识希望这篇文章能让你不仅有一定的收

热文推荐