24暑假算法刷题 | Day15 | LeetCode 110. 平衡二叉树,257. 二叉树的所有路径,404. 左叶子之和,222. 完全二叉树的节点个数

目录

  • 110. 平衡二叉树
    • 题目描述
    • 题解
  • 257. 二叉树的所有路径
    • 题目描述
    • 题解
  • 404. 左叶子之和
    • 题目描述
    • 题解
  • 222. 完全二叉树的节点个数
    • 题目描述
    • 题解


110. 平衡二叉树

点此跳转题目链接

题目描述

给定一个二叉树,判断它是否是平衡二叉树

平衡二叉树 是指该树所有节点的左右子树的深度相差不超过 1。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:true

示例 2:

img

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

示例 3:

输入:root = []
输出:true

提示:

  • 树中的节点数在范围 [0, 5000]
  • -104 <= Node.val <= 104

题解

显然,一个二叉树是平衡的,当且仅当它的所有子树都是平衡的。听起来就很适合递归秒了:

int getDepth(TreeNode *root)
{if (!root)return 0;return max(getDepth(root->left), getDepth(root->right)) + 1;
}bool isBalanced(TreeNode *root)
{if (!root)return true; // 空树是平衡树return abs(getDepth(root->left) - getDepth(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}

相应地,考虑一下迭代法。由于需要判断各节点“左右子树的深度差”是否大于1,联想到基于后序遍历实现本题:因为后序遍历处理某一节点时,总是已经访问过其左右子树节点了。

// 基于层序遍历获取某个节点的深度
int getDepth(TreeNode *root)
{if (!root)return 0;queue<TreeNode *> q;q.push(root);int depth = 0;while (!q.empty()){int size = q.size();for (int i = 0; i < size; i++){if (q.front()->left)q.push(q.front()->left);if (q.front()->right)q.push(q.front()->right);q.pop();}depth++;}return depth;
}// 基于后序遍历检验平衡树
bool isBalanced(TreeNode *root)
{if (!root)return true;// 统一迭代法的后序遍历stack<TreeNode *> st;st.push(root);while (!st.empty()){TreeNode *cur = st.top();st.pop();if (cur){st.push(cur);     // 中st.push(nullptr); // 空节点标记if (cur->left)st.push(cur->left); // 左if (cur->right)st.push(cur->right); // 右}else{if (abs(getDepth_II(st.top()->left) - getDepth_II(st.top()->right)) > 1)return false;st.pop();}}return true;
}

257. 二叉树的所有路径

点此跳转题目链接

题目描述

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例 1:

在这里插入图片描述

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例 2:

输入:root = [1]
输出:["1"]

提示:

  • 树中节点的数目在范围 [1, 100]
  • -100 <= Node.val <= 100

题解

首先考虑递归解法,有两种,一是传统的DFS(深度优先搜索):

void getPathDFS(TreeNode *root, string path, vector<string> &res)
{if (root){path += to_string(root->val);// 递归出口:遍历到叶子节点if (!root->left && !root->right)res.push_back(path);else{path += "->";getPathDFS(root->left, path, res);getPathDFS(root->right, path, res);}}
}vector<string> binaryTreePaths(TreeNode *root)
{vector<string> res;getPathDFS(root, "", res);return res;
}

二是结合回溯法,在基于递归的前序遍历框架下实现:

void traversal(TreeNode *root, vector<int> &paths, vector<string> &res)
{// 由于需要找到所有路径,采用前序遍历实现paths.push_back(root->val); // 中// 递归出口:遍历到叶子节点if (!root->left && !root->right){string path = to_string(paths[0]);for (int i = 1; i < paths.size(); ++i)path += "->" + to_string(paths[i]);res.push_back(path);return;}if (root->left){traversal(root->left, paths, res); // 左paths.pop_back();                  // 回溯}if (root->right){traversal(root->right, paths, res); // 右paths.pop_back();                   // 回溯}
}vector<string> binaryTreePaths(TreeNode *root)
{vector<string> res; // 最终的结果集vector<int> paths;  // 存储每条路径的数组(按照路径上节点的值)if (!root)return res;traversal(root, paths, res);return res;
}

其中每次回溯的作用相当于回退到上一个“分支点”,再选择一条与之前不同的分支进行操作,原理可以参考 代码随想录本题讲解 中的这张图,从始至终走一遍应该就能领会了:

在这里插入图片描述

最后还是考虑一下迭代法,同上面一样,要基于前序遍历的框架实现,具体来说就是在 统一迭代法 的基础上,新建一个存储当前路径的栈,随着“右左中”节点的入栈,相应的路径也要更新、入栈:

vector<string> binaryTreePaths(TreeNode *root)
{// 基于前序遍历的统一迭代法实现vector<string> res;stack<string> pathSt;stack<TreeNode *> nodeSt;if (!root)return res;nodeSt.push(root);pathSt.push(to_string(root->val));while (!nodeSt.empty()){TreeNode *node = nodeSt.top();nodeSt.pop();string path = pathSt.top();pathSt.pop();if (node){if (node->right){pathSt.push(path + "->" + to_string(node->right->val));nodeSt.push(node->right); // 右}if (node->left){pathSt.push(path + "->" + to_string(node->left->val));nodeSt.push(node->left); // 左}nodeSt.push(node);    // 中nodeSt.push(nullptr); // 空节点标记pathSt.push(path);    // 记录当前路径}else{if (!nodeSt.top()->left && !nodeSt.top()->right)res.push_back(path); // 已到叶子节点:当前路径加入结果集nodeSt.pop();}}return res;
}

⚠️ 值得注意的是,为了保证每次路径栈顶的路径与节点栈顶的节点“一一对应”,两个栈要同步操作(一起 pushpop 。唯一例外的是最后路径加入结果集时,节点栈 pop 了但是路径栈没有:

...
else
{if (!nodeSt.top()->left && !nodeSt.top()->right)res.push_back(path); // 已到叶子节点:当前路径加入结果集nodeSt.pop();
}

这是因为在每次循环一开始,两个栈就已经同时 pop 过了:

while (!nodeSt.empty())
{TreeNode *node = nodeSt.top();nodeSt.pop();string path = pathSt.top();pathSt.pop();...

而若要进入 else 、记录结果,上面这里节点栈弹出的是标记用的空节点,所以它自己最后还要再 pop 一次来弹出真正的节点,而路径栈就不用了。


404. 左叶子之和

点此跳转题目链接

题目描述

给定二叉树的根节点 root ,返回所有左叶子之和。

示例 1:

在这里插入图片描述

输入: root = [3,9,20,null,null,15,7] 
输出: 24 
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

示例 2:

输入: root = [1]
输出: 0

提示:

  • 节点数在 [1, 1000] 范围内
  • -1000 <= Node.val <= 1000

题解

比较简单,还是可以递归和迭代实现。要注意的就是判断左叶子只能通过其父节点判断:

root->left && !root->left->left && !root->left->right

即父节点有左孩子、且这个左孩子无左右孩子,则这个左孩子是左叶子。

递归法

int sumOfLeftLeaves(TreeNode *root)
{// 递归出口1:当前节点为空if (!root)return 0;int leftSum = sumOfLeftLeaves(root->left);// 递归出口2:当前节点的左孩子是左叶子if (root->left && !root->left->left && !root->left->right)leftSum = root->left->val;return leftSum + sumOfLeftLeaves(root->right);
}

迭代法

int sumOfLeftLeaves_II(TreeNode *root) {if (!root)return 0;queue<TreeNode*> q;q.push(root);int sum = 0;while (!q.empty()) {int size = q.size();for (int i = 0; i < size; ++i) {TreeNode *cur = q.front();if (cur->left) {q.push(cur->left);if (!cur->left->left && !cur->left->right)sum += cur->left->val;}if (cur->right)q.push(cur->right);q.pop();}}return sum;
}

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

点此跳转题目链接

题目描述

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例 1:

在这里插入图片描述

输入:root = [1,2,3,4,5,6]
输出:6

示例 2:

输入:root = []
输出:0

示例 3:

输入:root = [1]
输出:1

提示:

  • 树中节点的数目范围是[0, 5 * 104]
  • 0 <= Node.val <= 5 * 104
  • 题目数据保证输入的树是 完全二叉树

进阶: 遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?

题解

无脑算法当然就是遍历整棵树,记录节点个数即可(这样应该直接层序遍历最方便),时间复杂度为 O ( n ) O(n) O(n) ,不赘述。

考虑利用完全二叉树的性质提升速度。根据其性质,完全二叉树的子树中有很多都是满二叉树,而一个 n n n 层满二叉树的节点个数为 2 n − 1 2^n - 1 2n1 。所以我们可以利用这点,进行带剪枝的递归遍历:

  • 以当前节点为根,所得子树为满二叉树,则按公式计算节点数
  • 否则,递归计算节点数

其中,判断满二叉树的方法也很简单高效:看“最左”枝和“最右”枝的深度是否相同即可。

代码(C++)

int countNodes(TreeNode *root)
{if (!root)return 0;TreeNode *left = root->left;TreeNode *right = root->right;int leftDepth = 0, rightDepth = 0; // 左、右子树深度while (left) {left = left->left;leftDepth++;}while (right) {right = right->right;rightDepth++;}if (leftDepth == rightDepth) // 满二叉树,直接用公式计算return (2 << leftDepth) - 1;return countNodes(root->left) + countNodes(root->right) + 1;
}

这里用位运算, 2 << leftDepth 相当于 2 l e f t D e p t h + 1 2^{leftDepth + 1} 2leftDepth+1 ,其中+1是因为 leftDepth 是子树深度,还要加上根节点的1才是相应的树深度 n n n

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

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

相关文章

VMware Vsphere创建虚拟机

作者&#xff1a;红米 一、上传系统镜像 1、打开数据中心 2、新建文件夹&#xff0c;存放镜像 3、点击上传文件按钮 4、找到本地镜像上传 二、安装虚拟机 1、创建虚拟机 2、选择创建类型 3、为虚拟机命名并选择虚拟机安装的所在位置 4、选择计算资源 5、选择存储 6、选择兼容…

Linux系统部署MySQL数据库

1.Linux插入光盘&#xff0c;使用df-h获取光盘信息&#xff0c;默认/dev/sr0文件为光盘文件 使用命令 mount -o ro /dev/sr0 /media进行手动挂载 mount -o ro /dev/sr0 /media 2.进入cd /etc/yum.repos.d目录 编辑配置yum库&#xff0c;编辑vim yum.repos [BaseOS] nameba…

什么是IoC控制反转思想?

目录 一.什么是IoC&#xff1f; IoC核心思想 一.什么是IoC&#xff1f; IoC&#xff08;Inversion of Control&#xff09;即控制反转&#xff0c;这里的控制是代表控制权的意思&#xff0c;IoC是一种编程思想&#xff0c;旨在降低代码之间的耦合度、降低代码的维护成本。…

算法力扣刷题记录 五十二【617.合并二叉树】

前言 二叉树篇&#xff0c;继续。 记录 五十二【617.合并二叉树】 一、题目阅读 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要…

算法day04 位运算 插入排序 二分法 对数器

位运算: 1&#xff09;有一个数组只包含这样的数&#xff0c;有几个数出现偶数次&#xff0c;有1个数出现奇数次&#xff0c;要求时间复杂度不超过o(n),怎么求出现奇数次的数。 使用 ^ 异或运算整个数组&#xff0c;偶数次运算结果为0&#xff0c;只留下最后一个奇数次的数。 …

【元器件】二极管、三极管、MOS管

二极管 D 二极管是一种具有两个电极&#xff08;即正极和负极&#xff09;的电子器件。它是一种非线性元件&#xff0c;具有许多重要的功能和应用 三极管 Q 概述 一种控制电流的半导体器件&#xff0c;其作用是把微弱信号放大成幅度值较大的电信号&#xff0c;也用作无触点开…

鸿蒙语言基础类库:【@system.prompt (弹窗)】

弹窗 说明&#xff1a; 从API Version 8 开始&#xff0c;该接口不再维护&#xff0c;推荐使用新接口[ohos.prompt]。本模块首批接口从API version 3开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import prompt from system.prompt;…

6个高效再利用的UI作品集设计模板

UI 作品集是指用户界面设计师的个人作品集。它展示了设计师的设计能力、技巧和风格&#xff0c;也是充分展示他们设计能力的证明。优秀的UI 作品集应具有简洁明了、美观大方、良好的互动体验和明确的目标。本文将从两个方面的介绍 Ui 作品集模板的全部内容&#xff1a;UI 作品集…

JMX 反序列化漏洞

前言 前段时间看到普元 EOS Platform 爆了这个洞&#xff0c;Apache James&#xff0c;Kafka-UI 都爆了这几个洞&#xff0c;所以决定系统来学习一下这个漏洞点。 JMX 基础 JMX 前置知识 JMX&#xff08;Java Management Extensions&#xff0c;即 Java 管理扩展&#xff0…

叉车指纹一键启动/熄火车辆,“锁”住叉车安全

在现代工业领域&#xff0c;叉车作为重要的物流搬运工具&#xff0c;其安全性和便捷性一直是人们关注的焦点。为此&#xff0c;我们引入了一项技术——叉车指纹一键启动/熄火系统&#xff0c;真正实现了叉车安全的“锁定”。 这项技术不仅仅是简单的启动或关闭车辆的手段&#…

前端:Vue学习-2

前端&#xff1a;Vue学习-2 1. vue的生命周期2. 工程化开发和脚手架Vue CLI2.1 组件化开发2.2 scoped解决样式冲突2.3 data是一个函数2.4 组件通信2.5 非父子通信- event bus事件&#xff0c;provide&inject 3.v-model原理->实现父子组件双向绑定4. sync 修饰符->实现…

centos7 中tcp连接问题

centos对telnet过来的包没有响应 通过tcpdump查看到的TCP连接的不正常的报文&#xff0c;如下 通过tcpdump查看到的TCP连接的正常的报文 &#xff0c;如下 解决方法&#xff1a; cat /proc/sys/net/ipv4/tcp_tw_recycle cat /proc/sys/net/ipv4/tcp_timestamps 如果两个参数…

【Java算法】前缀和 下

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【算法工作坊】算法实战揭秘 一.连续数组 题目链接&#xff1a;525.连续数组 代码 public int findMaxLength(int[] nums) {HashMap<Integer,Integer> mapnew HashMap<>();map.put(0,-1);…

【系统架构设计师】十三、软件可靠性(基本概念|软件可靠性建模)

目录 一、基本概念 1.1 定义 1.2 软件可靠性的定量描述 1.3 可靠性测试的意义 1.4 广义的软件可靠性测试和狭义的软件可靠性测试 二、软件可靠性建模 2.1 可靠性模型的组成 2.2 可靠性模型的共同假设 2.3 可靠性模型的重要特性 2.4 可靠性建模方法 往期推荐 历年真…

SD-WAN组网搭建5G备份方案实现方式

SD-WAN&#xff08;Software-Defined Wide Area Network&#xff0c;软件定义广域网&#xff09;结合5G作为备份链路是现代企业网络弹性策略的一部分&#xff0c;尤其是在需要高可用性和快速故障切换的场景下。以下是实现SD-WAN组网并集成5G备份方案的一般步骤&#xff1a; 1. …

‍我想我大抵是疯了,我喜欢上了写单元测试

前言 大家好我是聪。相信有不少的小伙伴喜欢写代码&#xff0c;但是对于单元测试这些反而觉得多此一举&#xff0c;想着我都在接口文档测过了&#xff01;还要写什么单元测试&#xff01;写不了一点&#xff01;&#xff01; 由于本人也是一个小小程序猿&#x1f649;&#xf…

Python | 分享8个Excel自动化脚本,一定有你用得上的!

本文将介绍8个常用的Python脚本&#xff0c;帮助你轻松应对Excel的日常操作。那话不多说&#xff0c;开始吧&#xff01; 1. 安装所需的Python库 在开始之前&#xff0c;我们需要安装一些Python库来操作Excel文件。以下是需要安装的库&#xff1a; pandas&#xff1a;用于数据…

Java 实验七:集合的使用

一、实验目的 1、理解Java集合框架的特点、接口与类之间的关系&#xff1b; 2、掌握Java集合框架的List接口&#xff0c;以及List接口的重要实现类LinkedList、ArrayList&#xff1b; 3、掌握Java集合框架的Set、SortedSet接口&#xff0c;以及重要实现类HashSet 与 TreeSet…

活动回顾 | AutoMQ 联合 GreptimeDB 共同探讨新能源汽车数据基础设施

7 月 13 日&#xff0c;AutoMQ 携手 GreptimeDB“新能源汽车数据基础设施” 主题 meetup 在上海圆满落幕。本次论坛多角度探讨如何通过创新的数据管理和存储架构&#xff0c;提升汽车系统的性能、安全性和可靠性&#xff0c;从而驱动行业的持续发展和创新&#xff0c;涵盖 Auto…

C#字符串基本操作

1、代码 //1、创建字符串&#xff08;获取长度&#xff09;string str "Hello, World!";Console.WriteLine($"string:{str},length:{str.Length}");//2、字符串连接string str1 "Hello, ";string str2 "World!";Console.WriteLine…