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://www.mzph.cn/web/46325.shtml

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

相关文章

Redis-布隆过滤器(Bloom Filter)详解

文章目录 什么是布隆过滤器 布隆过滤器的优点&#xff1a;布隆过滤器的缺点&#xff1a;其他问题 布隆过滤器适合的场景布隆过滤器原理 数据结构增加元素查询元素删除元素 如何使用布隆过滤器 Google开源的Guava自带布隆过滤器Redis实现布隆过滤器 Redis中配置布隆过滤器Redis…

【echarts】tooltip 增加单位

单个柱子 const data [{value: 1,per: 2},{value: 22,per: 2},{value: 222,per: 3} ];tooltip: {trigger: axis,show: true,axisPointer: {type: line,lineStyle: {color: rgba(0, 0, 0, 0.03),type: solid,width: 60,},},formatter(params) {return ${params[0].name}: ${par…

数据结构历年考研真题对应知识点(树、森林)

目录 5.4.2树、森林与二叉树的转换 1.树转换为二叉树 【树和二叉树的转换及相关性质的推理(2009、2011)】 2.森林转换为二叉树 【森林和二叉树的转换及相关性质的推理(2014)】 3.二叉树转换为森林 【由遍历序列构造一棵二叉树并转换为对应的森林(2020、2021)】 5.4.3树和…

C# 各版本语法新功能汇总

C# 8.0 以后 官网 C# 7.3 》》in C# 7.2 》》 命名参数、具名参数 》》》 条件 ref 表达式 C# 7.1 》》 default 运算符 default 在C#7.1中得到了改进&#xff0c;不再需要default&#xff08;T&#xff09;了 //变量赋值C#7.0 var s "字符串"; s default(s…

LeetCode 算法:电话号码的字母组合 c++

原题链接&#x1f517;&#xff1a;电话号码的字母组合 难度&#xff1a;中等⭐️⭐️ 题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 …

SpringCloud教程 | 第九篇: 使用API Gateway

1、参考资料 SpringCloud基础篇-10-服务网关-Gateway_springcloud gateway-CSDN博客 2、先学习路由&#xff0c;参考了5.1 2.1、建了一个cloudGatewayDemo&#xff0c;这是用来配置网关的工程&#xff0c;配置如下&#xff1a; http://localhost:18080/aaa/name 该接口代码如…

git clone命令, 克隆远程仓库

这个应该是最简单的命令&#xff0c;当别人扔给你一个*****.git链接&#xff0c;你要知道怎么用&#xff0c;但是还需要注意以下几点&#xff1a; 1. 你在该网站上是否有账号 2. 你在该网站上的账号是否是该项目的成员&#xff0c;如果不是&#xff0c;那可能clone不了 3. 本机…

WSL-Ubuntu20.04部署环境配置

1.更换Ubuntu软件仓库镜像源 为了在WSL上使用TensorRT进行推理加速&#xff0c;需要安装以下环境&#xff0c;下面将按以下顺序分别介绍安装、验证以及删除环境&#xff1a; #1.C环境配置 gcc、gdb、g #2.gpu环境 cuda、cudnn #3.Cmake环境 CMake #4.OpenCV环境 OpenCV #5.Ten…

vxe-grid 实现配置式form搜索条件 form搜索条件框可折叠 配置式table

文章目录 效果图代码 效果图 代码 <template><div class"app-container"><vxe-grid refxGrid v-bind"gridOptions" v-if"tableHeight" :height"tableHeight"><template #billDate"{ data }"><e…

Zoom视频会议软件使用

Zoom是一款广受欢迎的视频会议软件&#xff0c;使用它可以轻松地进行远程会议、在线培训和团队协作等。要充分利用Zoom软件的功能&#xff0c;以下是详细具体的使用方法和步骤&#xff1a; 下载安装 下载&#xff1a;访问Zoom官方网站&#xff0c;根据使用的操作系统下载相应的…

ttkefu在线客服系统 机器人+人工客服 全渠道接入客户咨询

ttkefu在线客服系统是一种集成了机器人客服与人工客服&#xff0c;并支持全渠道接入客户咨询的综合解决方案。这种系统能够显著提升客户服务效率&#xff0c;优化客户体验&#xff0c;同时帮助企业降低运营成本 1. 智能机器人客服 自动回复&#xff1a;机器人客服能够自…

自制连点器

B站使用教程&#xff1a;https://www.bilibili.com/video/BV1SR85e4EKw/?vd_source47eba1800d831e86d4778a128740fe73 下载链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1Spv_yVPFB3zoS__VL-nhaQ?pwdyxo1 提取码&#xff1a;yxo1

20.x86游戏实战-远线程注入的实现

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

Spring AI 应用开发中设置访问 Ollama 的超时时间

使用 Spring AI 开发 AI 应用时&#xff0c;Ollama 通常在本地开发和测试时使用&#xff0c;用来在本地运行大模型。由于本地开发机器的资源限制&#xff0c;当使用 Ollama 运行较大的模型时&#xff0c;大模型给出响应的时间会比较长。Spring AI 提供的 OllamaChatModel 与 Ol…

在Mac上免费恢复误删除的Word文档

Microsoft Word for Mac是一个有用的文字处理应用程序&#xff0c;它与Microsoft Office套件捆绑在一起。该软件的稳定版本包括 Word 2019、2016、2011 等。 Word for Mac 与 Apple Pages 兼容;这允许在不同的操作系统版本中使用Word文档&#xff0c;而不会遇到任何麻烦。 与…

【数据结构】非线性表----树详解

树是一种非线性结构&#xff0c;它是由**n&#xff08;n>0&#xff09;**个有限结点组成一个具有层次关系的集合。具有层次关系则说明它的结构不再是线性表那样一对一&#xff0c;而是一对多的关系&#xff1b;随着层数的增加&#xff0c;每一层的元素个数也在不断变化&…

逆向案例二十三——请求头参数加密,某区块链交易逆向

网址&#xff1a;aHR0cHM6Ly93d3cub2tsaW5rLmNvbS96aC1oYW5zL2J0Yy90eC1saXN0L3BhZ2UvNAo 抓包分析&#xff0c;发现请求头有X-Apikey参数加密&#xff0c;其他表单和返回内容没有加密。 直接搜索关键字&#xff0c;X-Apikey&#xff0c;找到疑似加密位置&#xff0c;注意这里…

零基础学习Python(三)

1. 多重继承 一个子类可以继承多个父类&#xff0c;这与一些编程语言的规则不通。 如果多个父类中有同名的变量和方法&#xff0c;子类访问的顺序是按照继承时小括号里书写的顺序进行访问的。 可以用issubclass(B, A)方法判断B是否为A的子类。 2. 绑定 类中的方法通过参数s…

《TF2.x强化学习手册》P59-P65-SARSA-Q-learning

文章目录 实现SARSA算法和对应的强化学习智能体前期准备实现步骤工作原理初始化算法流程 构建基于Q学习的智能体前期准备实现步骤工作原理SARSA 算法的收敛性&#xff1a;SARSA 适合在线学习和真实系统&#xff1a;Q 学习算法的适用性&#xff1a; 实现SARSA算法和对应的强化学…

HDC使用常见命令

HDC&#xff08;HarmonyOS Device Connector&#xff09;是为开发人员提供的用于调试的命令行工具&#xff0c;通过该工具可以在windows/linux/mac系统上与真实设备进行交互。 使用HDC前&#xff0c;需要配置相关环境变量&#xff1a; 在此电脑 > 属性 > 高级系统设置 &g…