算法通关村第八关——轻松搞定二叉树的深度和高度问题

news2024/11/15 20:07:23

1.基础知识

二叉树节点的高度:指从当前节点到叶子节点的最长简单路径边的条数

二叉树节点的深度:指从根节点到当前节点的最长简单路径边的条数

二叉树的深度和高度问题,递归思想的运用很是普遍,有的问题层序遍历也可以解决。

在这里插入图片描述

2.最大深度问题

力扣104题,给定一个二叉树,找出最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明:叶子节点是指没有子节点的节点。

递归的三步走:输入输出参数、递归终止条件、完整的递归逻辑。

在这里插入图片描述

分析:对于rootNode(7),最大深度很明显是左右子节点深度+1,当然,左右子节点存在为空的情况,但只要有一个不为空,树的最大高度就是1+1=2。再增加几个节点:

在这里插入图片描述

对于node(3),最大深度仍然是左右子节点深度+1,左右子节点只要有一个不为空,node(3)的最大高度是1+1=2。而对于rootNode(7),则是左右子树中深度最大的一个+1,于是对于rootNode(7)的判断逻辑用代码表示就是:

let leftDepth = getDepth(root.left);
let rightDepth = getDepth(root.right);
let maxDepth = Math.max(leftDepth, rightDepth) + 1;

递归终止的条件就是root === null然后返回0就行。输入参数就是子树的根节点root,返回值为当前root所在子树的最大高度。

function maxDepth(root) {
	if (root === null) {
		return 0;
	}

	// 得到根节点左右子树的最大深度
	let leftDepth = maxDepth(root.left);
	let rightDepth = maxDepth(root.right);

	return Math.max(leftDepth, rightDepth) + 1;
}

仔细观察可以发现,先拿到左右子树的结果,再计算根节点的深度,这与后序遍历本质一样。

层序遍历也可以得到最大深度,方法就是每遍历一层,设置的记录层数的变量+1即可。代码如下:

function maxDepth(root) {
	if (root === null) {
		return 0;
	}

	let maxDepth = 0;
	const treeQueue = [root];

	while (treeQueue.length !== 0) {
        // lenOfTreeQueue表示每一层的元素数
		lenOfTreeQueue = treeQueue.length;
		// lenOfTreeQueue=0表示一层访问完了
		while (lenOfTreeQueue > 0) {  
            let treeNode = treeQueue.pop();
            if (treeNode.left !== null) {
                treeQueue.push(treeNode.left);
            }

            if (treeNode.right !== null) {
                treeQueue.push(treeNode.left);
            }
            lenOfTreeQueue--;
        }
		maxDepth++;
	}
	return maxDepth;
}

3 判断平衡二叉树

力扣110题,给定一个二叉树,判断它是否是高度平衡的二叉树。本题中高度平衡二叉树定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。

关于根节点的深度究竟是1 还是 0,不同的地方有不一样的标准,leetcode的题目中都是以根节点深度是1,我们以leetcode为准。

在这里插入图片描述

分析:对于rootNode(7),左右孩子如果只有一个,高度差就是1;如果左右孩子都有或者都没有,则高度差为0。再增加一层。

在这里插入图片描述

对于rootNode(7),需要知道它的左右子树的最大高度差是否 < 2

  • root节点的左右子树高度差 < 2,就返回左右子树最大高度+1
  • root节点左右子树的高度差 >= 2,就返回-1,表示此子树不是平衡树。

用代码来表示就是:

let leftHeight = getHeight(root.left);
let rightHeight = getHeight(root.right);
return Math.abs(leftHeight - rightHeight) < 2 ? Math.max(leftHeight, rightHeight) + 1 : -1;

完整代码如下:

function isBalanced(root) {

	if (nodeHeight(root) >= 0) {
		return true;
	} else return false;


	function nodeHeight(root) {
		if (root === null) {
			return false;
		}

		let leftNodeHeight = nodeHeight(root.left);
		let rightNodeHeight = nodeHeight(root.right);

		// 若当前节点左右子树的高度差<2,返回当前节点的左右子树最大高度+1
		// 若当前节点左右子树的高度差>=2,返回-1
		if (leftNodeHeight === -1 || 
			rightNodeHeight === -1 || 
			Math.abs(leftNodeHeight - rightNodeHeight) >= 2) {
			return -1;
		} else {
			return Math.max(leftNodeHeight, rightNodeHeight) + 1;
		}
	}
}

4.最小深度问题

力扣111题,给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量,例如下面的例子返回结果为2。说明:叶子节点是指没有子节点的节点。

在这里插入图片描述

最小深度的一层必须是要到叶子节点

分析终止条件:

  • 如果左子树为空,右子树不为空,说明最小深度是1+右子树的深度
  • 相反,如果右子树为空,左子树不为空,最小深度就是1+左子树的深度
  • 如果左右子树都不为空,返回左右子树深度中最小值+1.

代码如下:

function minDepth(root) {
	if (root === null) {
		return 0;
	}

	// 终止条件有三种
	// 1.当前节点的左右子节点都为空,说明到达了叶子节点,leftDepth和rightDepth为0
	// 2.左右子树其中一个为空,leftDepth和rightDepth其中一个为0,就返回比较大的那个子树深度
	let leftDepth = minDepth(root.left);
	let rightDepth = minDepth(root.right);

	if {root.left === null || root.right === null} {
		return leftDepth + rightDepth + 1;
	}

	// 3.最后一种情况,也就是左右孩子都不为空,返回左右子树最小深度+1
	return Math.min(leftDepth, rightDepth) + 1;
}

这道题同样也能用层序遍历来解决。遍历时,第一次遇到叶子就直接返回其所在层次即可:

function minDepth(root) {
	if (root === null) {
		return 0;
	}

	let minDepth = 0;
	const treeQueue = [root];

	while (treeQueue.length !== 0) {
		lenOfTreeQueue = treeQueue.length;
		minDepth++;
		let treeNode = treeQueue.pop();
		// 如果左右子节点均为空,说明是叶子节点,直接返回
		if (treeNode.left === null && treeNode.right === null) {
			return minDepth;
		}
		
		if (treeNode.left !== null) {
			treeQueue.push(treeNode.left);
		}

		if (treeNode.right !== null) {
			treeQueue.push(treeNode.left);
		}

	}

5.N叉树的最大深度

力扣559题,给定一个 N 叉树,找到其最大深度。最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔,比如:

示例1:
输入:root = [1,null,3,2,4,null,5,6]
输出:3
示例2:
输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:5

分析:N叉树与二叉树不同之处就在于N叉树节点比较多,root的子节点为N个,也就是N个子树。记N个子树的最大深度中的最大值为maxChildDepth,那么该N叉树的最大深度为maxChildDepth + 1

var maxDepth = function(root) {	
    if (root === null) {
        return 0;
    } else if (root.children === null) {
        return 1;
    }

    let maxChildDepth = 0;
    const children = root.children;
    for(const child of children) {
        // 记录当前子树的最大深度
        const childDepth = maxDepth(child);
        // 将当前子树的最大深度与记录的最大子树深度进行比较
        maxChildDepth = Math.max(maxChildDepth, childDepth);
    }

    return maxChildDepth + 1; 
}

用层次遍历来解决的话,我们每次从队列里拿出当前层的所有节点,这样下一次遍历的就是当前层的所有子节点,以此类推。设置一个记录遍历层数的变量。

function maxDepth(root) {
	if (root === null) {
		return 0;
	} else if (root.children === null) {
		return 1;
	}

	const treeQueue = [root];
	let maxDepth = 0;

	while (treeQueue.length !== 0) {
		// 记录每层所有节点数
		let lenOfTreeQueue = treeQueue.length;
		while (lenOfTreeQueue > 0) {
			const node = treeQueue.shift();
			const children = node.children;
			for (const child of children) {
				treeQueue.push(child);
			}
			lenOfTreeQueue--;
		}
		maxDepth++;
	}
	return maxDepth
}

总结

牢记递归三步走:

  1. 从局部到整体递推
  2. 分情况讨论,明确递归终止条件
  3. 得出完整的递归逻辑

如果想验证的话,就从整体到局部画图推演。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/900353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PyTorch Lightning:通过分布式训练扩展深度学习工作流

一、介绍 欢迎来到我们关于 PyTorch Lightning 系列的第二篇文章&#xff01;在上一篇文章中&#xff0c;我们向您介绍了 PyTorch Lightning&#xff0c;并探讨了它在简化深度学习模型开发方面的主要功能和优势。我们了解了 PyTorch Lightning 如何为组织和构建 PyTorch 代码提…

3种获取OpenStreetMap数据的方法【OSM】

OpenStreetMap 是每个人都可以编辑的世界地图。 这意味着你可以纠正错误、添加新地点&#xff0c;甚至自己为地图做出贡献&#xff01; 这是一个社区驱动的项目&#xff0c;拥有数百万注册用户。 这是一个社区驱动的项目&#xff0c;旨在在开放许可下向每个人提供所有地理数据。…

基于YOLOv8模型的奶牛目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的奶牛目标检测系统可用于日常生活中检测与定位奶牛目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数据集…

【小梦C嘎嘎——启航篇】vector 以及日常使用的接口介绍

【小梦C嘎嘎——启航篇】vector 日常使用的接口介绍&#x1f60e; 前言&#x1f64c;vector 是什么&#xff1f;vector 比较常使用的接口 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01…

Parking Steps

上面是老师傅说的停车步骤&#xff0c;说这样不会伤变速箱。 平时就是&#xff0c;脚踩刹车&#xff0c;直接从D档撸到P档&#xff0c;拉手刹&#xff0c;哈哈。 你的停车步骤是啥。。

redis 存储结构原理 2

咱们接着上一部分来进行分享&#xff0c;我们可以在如下地址下载 redis 的源码&#xff1a; https://redis.io/download 此处我下载的是 redis-6.2.5 版本的&#xff0c;xdm 可以直接下载上图中的 **redis-6.2.6 **版本&#xff0c; redis 中 hash 表的数据结构 redis hash …

php_mb_strlen指定扩展

1 中文在utf-字符集下占3个字节,所以计算出来长度为9。 2 可以引入php多字节字符的扩展&#xff0c;默认是没有的&#xff0c;需要自己配置这个函数 3 找到php.ini文件&#xff0c;去掉;extension mbstring的注释&#xff0c;接着重启apache服务 可以看到准确输出的中文的长度…

javascript期末作业【三维房屋设计】

1、引入three.js库 官网下载three.js 库 放置目录并引用 引入js文件: 设置场景&#xff08;scene&#xff09; &#xff08;1&#xff09;创建场景对象 &#xff08;2&#xff09;设置透明相机 1,透明相机的优点 透明相机机制更符合于人的视角,在场景预览和游戏场景多有使用…

视频怎么转gif高清动图?分享一款视频转gif工具

许多小伙伴都不知道如何将拍摄的短视频转gif图片&#xff0c;本文将分享一款专业的视频转gif工具&#xff0c;打来浏览器即可将视频在线转gif&#xff08;https://www.gif.cn&#xff09;&#xff0c;操作简单&#xff0c;使用方便&#xff0c;下面是详细的步骤。 打开网站&am…

SpringBoot案例-员工管理-新增员工

查看页面原型&#xff0c;明确需求 页面原型 需求 阅读接口文档 接口文档链接如下&#xff1a; 【腾讯文档】SpringBoot案例所需文档 https://docs.qq.com/doc/DUkRiTWVaUmFVck9N 思路分析 阅读需求文档后可知&#xff0c;前端发送请求的同时&#xff0c;将前端请求参数以…

centos8 使用phpstudy安装tomcat部署web项目

系统配置 1、安装Tomcat 2、问题 正常安装完Tomcat应该有个配置选项&#xff0c;用来配置server.xml web.xml 还有映射webapps路径选项&#xff0c;但是我用的这个版本并没有。所以只能曲线救国。 3、解决 既然没有配置项&#xff0c;那就只能按最基本的方法配置&#xff0c…

算法之排序总结

排序算法 最近&#xff0c;一直在学习业务上的知识&#xff0c;对基础没有怎么重视&#xff0c;因此&#xff0c;这篇文章想对于排序算法进行一个大致的总结&#x1f913;&#x1f913;&#x1f913;。 首先来说一下&#xff0c;关于排序一些相关的基础知识。 排序概述 原地…

代码随想录第25天|216.组合总和III ​​​​​​​,17. 电话号码的字母组合

216.组合总和III 回溯三部曲 确定递归函数参数 targetSum&#xff08;int&#xff09;目标和&#xff0c;也就是题目中的n。k&#xff08;int&#xff09;就是题目中要求k个数的集合。sum&#xff08;int&#xff09;为已经收集的元素的总和&#xff0c;也就是path里元素的…

(学习笔记-进程管理)什么是悲观锁、乐观锁?

互斥锁与自旋锁 最底层的两种就是 [互斥锁和自旋锁]&#xff0c;有很多高级的锁都是基于它们实现的。可以认为它们是各种锁的地基&#xff0c;所以我们必须清楚它们之间的区别和应用。 加锁的目的就是保证共享资源在任意时间内&#xff0c;只有一个线程访问&#xff0c;这样就…

LabVIEW模拟化学反应器的工作

LabVIEW模拟化学反应器的工作 近年来&#xff0c;化学反应器在化学和工业过程领域有许多应用。高价值产品是通过混合产品&#xff0c;化学反应&#xff0c;蒸馏和结晶等多种工业过程转换原材料制成的。化学反应器通常用于大型加工行业&#xff0c;例如酿酒厂公司饮料产品的发酵…

C 基础拾遗

C基础拾遗 预处理器 预处理器 14.1 预定义符号 14.2 #define

5种常见的3D游戏艺术风格及工具栈

在游戏开发领域&#xff0c;3D 艺术风格已成为为玩家创造身临其境、引人入胜的体验的重要组成部分。 随着技术的进步&#xff0c;创造令人惊叹的 3D 视觉效果的可能性已经大大扩展&#xff0c;为游戏开发人员提供了广泛的选择。 在本文中&#xff0c;我们将探讨当今游戏开发中…

Seaborn数据可视化(一)

目录 1.seaborn简介 2.Seaborn绘图风格设置 21.参数说明&#xff1a; 2.2 示例&#xff1a; 1.seaborn简介 Seaborn是一个用于数据可视化的Python库&#xff0c;它是建立在Matplotlib之上的高级绘图库。Seaborn的目标是使绘图任务变得简单&#xff0c;同时产生美观且具有信…

micropython SSD1306/SSD1315驱动

目录 简介 代码 功能 显示ASCII字符 ​编辑 画任意直线 画横线 画竖线 画矩形 画椭圆 画立方体 画点阵图 翻转 反相 滚动 横向滚动 纵向滚动 奇葩滚动 简介 我重新写了一个驱动&#xff0c;增加了一些功能&#xff0c;由于我的硬件是128*64oled单色I2C&#xff0c;我只…

lvs-DR模式:

lvs-DR数据包流向分析 客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。 Director Server 和 Real Server 在同一个网络中&#xff0c;数据通过二层数据链路…