代码随想录阅读笔记-二叉树【平衡二叉树】

题目

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

110.平衡二叉树

返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

110.平衡二叉树1

返回 false 。

思路 

很多人一看题目觉得和求最大深度很像,其实有很大区别。

这里强调一波概念:

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。

但leetcode中强调的深度和高度很明显是按照节点来计算的,如图:

110.平衡二叉树2

关于根节点的深度究竟是1 还是 0,不同的地方有不一样的标准,leetcode的题目中都是以节点为一度,即根节点深度是1。但维基百科上定义用边为一度,即根节点的深度是0,我们暂时以leetcode为准。

因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)

有的同学一定疑惑,为什么之前求最大深度中求的是二叉树的最大深度,也用的是后序遍历。

那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。

在求二叉树最大深度中,如果真正求取二叉树的最大深度,代码应该写成如下:(前序遍历)

class Solution {
public:int result;void getDepth(TreeNode* node, int depth) {result = depth > result ? depth : result; // 中if (node->left == NULL && node->right == NULL) return ;if (node->left) { // 左depth++;    // 深度+1getDepth(node->left, depth);depth--;    // 回溯,深度-1}if (node->right) { // 右depth++;    // 深度+1getDepth(node->right, depth);depth--;    // 回溯,深度-1}return ;}int maxDepth(TreeNode* root) {result = 0;if (root == NULL) return result;getDepth(root, 1);return result;}
};

可以看出使用了前序(中左右)的遍历顺序,这才是真正求深度的逻辑!

注意以上代码是为了把细节体现出来,简化一下代码如下:

class Solution {
public:int result;void getDepth(TreeNode* node, int depth) {result = depth > result ? depth : result; // 中if (node->left == NULL && node->right == NULL) return ;if (node->left) { // 左getDepth(node->left, depth + 1);}if (node->right) { // 右getDepth(node->right, depth + 1);}return ;}int maxDepth(TreeNode* root) {result = 0;if (root == 0) return result;getDepth(root, 1);return result;}
};

本题思路

递归

此时大家应该明白了既然要求比较高度,必然是要后序遍历。

递归三步曲分析:

1、明确递归函数的参数和返回值

参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。

那么如何标记左右子树是否差值大于1呢?

如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。

所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。

// -1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度
int getHeight(TreeNode* node)

2、明确终止条件

递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0

if (node == NULL) {return 0;
}

3、明确单层递归的逻辑

如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。

int leftHeight = getHeight(node->left); // 左
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right); // 右
if (rightHeight == -1) return -1;int result;
if (abs(leftHeight - rightHeight) > 1) {  // 中result = -1;
} else {result = 1 + max(leftHeight, rightHeight); // 以当前节点为根节点的树的最大高度
}return result;

代码精简之后如下:

int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);

此时递归的函数就已经写出来了,这个递归的函数传入节点指针,返回以该节点为根节点的二叉树的高度,如果不是二叉平衡树,则返回-1。

最后本题整体递归代码如下:

class Solution {
public:// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1int getHeight(TreeNode* node) {if (node == NULL) {return 0;}int leftHeight = getHeight(node->left);if (leftHeight == -1) return -1;int rightHeight = getHeight(node->right);if (rightHeight == -1) return -1;return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);}bool isBalanced(TreeNode* root) {return getHeight(root) == -1 ? false : true;}
};

迭代

在求二叉树的最大深度中我们可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度了,这就体现出求高度和求深度的不同。

本题的迭代方式可以先定义一个函数,专门用来求高度。

这个函数通过栈模拟的后序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度)

代码如下:

// cur节点的最大深度,就是cur的高度
int getDepth(TreeNode* cur) {stack<TreeNode*> st;if (cur != NULL) st.push(cur);int depth = 0; // 记录深度int result = 0;while (!st.empty()) {TreeNode* node = st.top();if (node != NULL) {st.pop();st.push(node);                          // 中st.push(NULL);depth++;if (node->right) st.push(node->right);  // 右if (node->left) st.push(node->left);    // 左} else {st.pop();node = st.top();st.pop();depth--;}result = result > depth ? result : depth;}return result;
}

然后再用栈来模拟后序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,代码如下:

bool isBalanced(TreeNode* root) {stack<TreeNode*> st;if (root == NULL) return true;st.push(root);while (!st.empty()) {TreeNode* node = st.top();                       // 中st.pop();if (abs(getDepth(node->left) - getDepth(node->right)) > 1) { // 判断左右孩子高度是否符合return false;}if (node->right) st.push(node->right);           // 右(空节点不入栈)if (node->left) st.push(node->left);             // 左(空节点不入栈)}return true;
}

整体代码如下:

class Solution {
private:int getDepth(TreeNode* cur) {stack<TreeNode*> st;if (cur != NULL) st.push(cur);int depth = 0; // 记录深度int result = 0;while (!st.empty()) {TreeNode* node = st.top();if (node != NULL) {st.pop();st.push(node);                          // 中st.push(NULL);depth++;if (node->right) st.push(node->right);  // 右if (node->left) st.push(node->left);    // 左} else {st.pop();node = st.top();st.pop();depth--;}result = result > depth ? result : depth;}return result;}public:bool isBalanced(TreeNode* root) {stack<TreeNode*> st;if (root == NULL) return true;st.push(root);while (!st.empty()) {TreeNode* node = st.top();                       // 中st.pop();if (abs(getDepth(node->left) - getDepth(node->right)) > 1) {return false;}if (node->right) st.push(node->right);           // 右(空节点不入栈)if (node->left) st.push(node->left);             // 左(空节点不入栈)}return true;}
};

当然此题用迭代法,其实效率很低,因为没有很好的模拟回溯的过程,所以迭代法有很多重复的计算。

虽然理论上所有的递归都可以用迭代来实现,但是有的场景难度可能比较大。

例如:都知道回溯法其实就是递归,但是很少人用迭代的方式去实现回溯算法!

因为对于回溯算法已经是非常复杂的递归了,如果再用迭代的话,就是自己给自己找麻烦,效率也并不一定高。

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

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

相关文章

ZKFair 创新之旅,新阶段如何塑造财富前景

在当前区块链技术的发展中&#xff0c;Layer 2&#xff08;L2&#xff09;解决方案已成为提高区块链扩容性、降低交易成本和提升交易速度的关键技术&#xff0c;但它仍面临一些关键问题和挑战&#xff0c;例如用户体验的改进、跨链互操作性、安全性以及去中心化程度。在这些背景…

Unity中UI系统1——GUI

介绍 工作原理和主要作用 基本控件 a.文本和按钮控件 练习&#xff1a; b.多选框和单选框 练习&#xff1a; 用的是第三种方法 c.输入框和拖动框 练习&#xff1a; 练习二&#xff1a; e.图片绘制和框 练习&#xff1a; 复合控件 a.工具栏和选择网格 练习&#xff1a; b.滚动视…

纷享销客如何向生态型CRM进化 创始人罗旭给出了答案

自己挣1块钱时&#xff0c;渠道合作伙伴能够挣1块甚至更多。这是纷享销客与生态共建之道。 2024年纷享销客北方战区渠道生态伙伴发展共建会于日前在北京举行。在这场主题为“聚力纷享共赢巅峰”的大会上&#xff0c;各方探讨了企业高质量增长之源与SaaS行业渠道发展之路&#…

云渲染实用工具:3ds max怎么改低版本?

3ds Max是建模领域广泛采用的专业软件&#xff0c;它通过定期更新来不断增强功能和提升性能。但这些频繁的更新有时会导致一些插件暂时无法与新版本完全兼容。为了解决这个问题&#xff0c;设计师们可以采用一个简单有效的方法&#xff0c;那就是将较新版本的3ds Max文件进行版…

基于Unity+Vue3通信交互的WebGL项目发布实践

基于UnityVue3通信交互的WebGL项目发布实践 实践路线 基于UnityVue3通信交互的WebGL项目发布实践问题背景准备工作解决方案项目实践小目标搭建Unity测试项目 创建Vue3测试项目运行项目验证unity和vue通信功能总结与展望 问题背景 我们最近需要把unity开发的pc项目迁移到web端&…

吴恩达深度学习笔记:浅层神经网络(Shallow neural networks)3.9-3.11

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第三周&#xff1a;浅层神经网络(Shallow neural networks)3.9 神 经 网 络 的 梯 度 下 降 &#xff08; Gradient descent for neural networks&#xff09;3.10&#xff08;选修&#xff0…

C#让标题栏闪烁着动起来的方法

目录 1.API函数FlashWindow &#xff08;1&#xff09;添加命名空间 &#xff08;2&#xff09;声明DllImport方法 &#xff08;3&#xff09;FlashWindow函数 2.P/Invoke 3.再来一个示例 在Windows系统中&#xff0c;当程序在后台运行时&#xff0c;如果某个窗体的提示信…

环境配置——已解决ModuleNotFoundError: No module named ‘cv2’(python)

一、报错代码 在网上搜到不少用Python处理图形的代码&#xff0c;于是复制别人的代码直接运行却报错&#xff0c;得到的结果却是&#xff1a;已解决ModuleNotFoundError: No module named ‘cv2’。&#xff08;当时心里瞬间凉了一大截&#xff0c;最后顺利解决了&#xff0c;顺…

软考 系统架构设计师系列知识点之云原生架构设计理论与实践(8)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之云原生架构设计理论与实践&#xff08;7&#xff09; 所属章节&#xff1a; 第14章. 云原生架构设计理论与实践 第2节 云原生架构内涵 14.2 云原生架构内涵 关于云原生的定义有众多版本&#xff0c;对于云原生架构的…

脑部肿瘤检测YOLOV8

脑部肿瘤检测&#xff0c;采用YOLOV8训练得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV调用&#xff0c;支持C/PYTHON/ANDORID开发脑部肿瘤检测YOLOV8

如何在Plesk面板备份网站

本周有一个客户&#xff0c;购买Hostease的Windows虚拟主机&#xff0c;咨询我们的在线客服&#xff0c;询问Windows虚拟主机Plesk面板是否提供备份功能。我们为用户提供教程&#xff0c;用户很快完成了数据备份。在此&#xff0c;我们分享这个操作教程&#xff0c;希望可以对您…

实践笔记-harbor-01搭建(版本:2.9.0)

harbor搭建 1.下载安装包&#xff08;版本&#xff1a;2.9.0&#xff09;2.修改配置文件3.安装4.访问harbor5.可能用得上的命令: 环境&#xff1a;centos7 1.下载安装包&#xff08;版本&#xff1a;2.9.0&#xff09; 网盘资源&#xff1a;https://pan.baidu.com/s/1fcoJIa4x…

多微信聚合聊天神器,让你的社交更高效!

对于那些拥有多个微信号的用户来说&#xff0c;频繁地在不同微信号和设备之间切换既麻烦又容易搞混。这时候&#xff0c;一款多微信聚合聊天神器——微信管理系统应运而生&#xff0c;为我们带来了极大的便利与高效。 下面一起来看看它都有哪些功能吧&#xff01; 1、多微信同…

mybatis plus 的方法有些带填充和逻辑删除,有些又不带

有没有人知道这是怎么个规律。逻辑删除看了下都会自动带上&#xff0c;但是主要是删除和修改方法&#xff0c;有些会按照设置的handler自动填充&#xff0c;有些又不会。有大佬知道吗。

C++ | Leetcode C++题解之第2题两数相加

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {map<int,int> a;//提供一对一的hashvector<int> b(2,-1);//用来承载结果&#xff0c;初始化一个大小为2&#xff0c;值为-1的容…

软文推广4大坑,媒介盒子告诉你

今天媒介盒子来和大家聊聊软文推广中的4个坑&#xff0c;让企业在软文推广过程中少走弯路。 一、 没有目的 在如今这个“内容为王”的时代&#xff0c;对软文推广而言&#xff0c;不管其目的性是增强用户参与度、提升品牌认知度还是促成转化等&#xff0c;但总归一个原则&…

AI工作站设计方案:903-多路PCIe3.0的单CPU 学习型AI工作站

多路PCIe3.0的单CPU 学习型AI工作站 一、机箱功能和技术指标&#xff1a; 系统 系统型号 ORI-SR500 主板支持 EEB(12*13)/CEB(12*10.5)/ATX(12*9.6)/Mi cro ATX 前置硬盘 最大支持2个3.5寸1个2.5寸SATA 硬盘2个2.5寸SATA 硬盘 &#xff08;背部&#xff09; 电源类型 C…

探索父进程和子进程

文章目录 通过系统调用查看进程PID父进程、子进程 通过系统调用创建进程-fork初识为什么fork给父进程返回子进程的PID&#xff0c;给子进程返回0fork函数如何做到返回两个值一个变量为什么同时会有两个返回值&#xff1f;bash总结 通过系统调用查看进程PID getpid()函数可以获…

【二叉树】Leetcode 101. 对称二叉树【简单】

对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 解题思路 判断一棵二叉树是否是轴对称的&#xff0c;可以通过递归方式进行判断。 1、定义一个递归函数isMirr…

SpringSecurity学习总结(三更草堂)

SpringSecurity安全框架的核心功能是认证和授权&#xff1a; 认证&#xff1a;验证当前访问系统的是不是本系统的用户&#xff0c;并且要确认具体是哪个用户。 授权&#xff1a;经过认证后判断当前用户是否具有进行某个操作的权限。 一般来说中大型的项目都是使用SpringSecurit…