6.二叉树.题目1

6.二叉树.题目

  • 题目
    • 1.翻转二叉树
    • 2.对称二叉树
    • 3.二叉树的最大深度
    • 4.二叉树的最小深度
    • 5.完全二叉树的节点个数
    • 6.平衡二叉树
    • 7.二叉树的所有路径
    • 8.左叶子之和
  • 总结

题目

1.翻转二叉树

(题目链接)
直观的思路是就把每一个节点的左右孩子交换一下就可以了,
深度优先-递归法
前序,后序遍历方法都没有问题,但中序的递归法会存在问题。

深度优先-迭代法
在迭代法-统一写法下,前序,后序遍历方法都没有问题,就是在while循环else接口处设置std::swap()函数交换左右子节点的位置。但是唯独中序遍历不方便,因为中序遍历会把某些节点的左右子节点翻转了两次。解释:因为中序遍历的特殊点:不同于前序,后序在遍历过程中在一开始,或结尾处交换左右子节点的位置,而中序在交换左,右子节点中间调换了左右子节点,所以第二个递归参数应该是现在的左子树才是原来的右子树。

    TreeNode* invertTree(TreeNode* root) {stack<TreeNode*> st;if (root != NULL) st.push(root);while (!st.empty()) {TreeNode* node = st.top();if (node != NULL) {st.pop();if (node->right) st.push(node->right);  // 右if (node->left) st.push(node->left);    // 左st.push(node);                          // 中st.push(NULL);} else {st.pop();node = st.top();st.pop();swap(node->left, node->right);          // 节点处理逻辑}}return root;}

广度优先-迭代法
在层序遍历的基础上,对每个queue.top()节点使用设置std::swap()函数交换左右子节点的位置。

    TreeNode* invertTree(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);while (!que.empty()) {int size = que.size();for (int i = 0; i < size; i++) {TreeNode* node = que.front();que.pop();swap(node->left, node->right); // 节点处理if (node->left) que.push(node->left);if (node->right) que.push(node->right);}}return root;}

2.对称二叉树

(题目链接)
给定一个二叉树,检查它是否是镜像对称的。对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树
在这里插入图片描述
递归法
除了判断子树的节点数值是否相等,还得处理节点为nullptr的情况

  • 左节点为空,右节点不为空,不对称,return false
  • 左节点不为空,右节点为空,不对称 return false
  • 左右节点都为空,对称,返回true

然后就可以单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况

  • 比较二叉树外侧是否对称:传入的是左节点的左节点,右节点的右节点。
  • 比较内侧是否对称,传入左节点的右节点,右节点的左节点。
  • 如果左右节点都对称就返回true ,有一侧不对称就返回false,使用与逻辑运算符
    bool compare(TreeNode* left, TreeNode* right){// 处理不同左右节点的情况的返回值if(left == nullptr && right != nullptr) return false;else if(left != nullptr && right == nullptr) return false;else if(left == nullptr && right == nullptr) return true;else if(left->val != right->val) return false;bool outside = compare(left->left, right->right);bool intside = compare(left->right, right->left);bool isame = outside && intside;return isame;}bool isSymmetric(TreeNode* root) {if(root==nullptr) return true;return compare(root->left, root->right);}

深度优先-迭代法
迭代法,其实是把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较,那么其实使用栈也是可以的,主要是比较的子节点的逻辑不错:left->leftright->rightleft->rightright->left进行比较,使用队列queue,堆stack都能实现该功能。

    bool isSymmetric(TreeNode* root) {if (root == NULL) return true;queue<TreeNode*> que;que.push(root->left);   // 将左子树头结点加入队列que.push(root->right);  // 将右子树头结点加入队列while (!que.empty()) {  // 接下来就要判断这两个树是否相互翻转TreeNode* leftNode = que.front(); que.pop();TreeNode* rightNode = que.front(); que.pop();if (!leftNode && !rightNode) {  // 左节点为空、右节点为空,此时说明是对称的continue;}// 左右一个节点不为空,或者都不为空但数值不相同,返回falseif ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {return false;}que.push(leftNode->left);   // 加入左节点左孩子que.push(rightNode->right); // 加入右节点右孩子que.push(leftNode->right);  // 加入左节点右孩子que.push(rightNode->left);  // 加入右节点左孩子}return true;}

3.二叉树的最大深度

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

这题在二叉树的层序遍历的求二叉树最大深度里提及到了。
深度优先-递归法

    int count_depth(TreeNode* root){if(root == nullptr) return 0;int leftdepth = count_depth(root->right);int rightdepth = count_depth(root->left);int depth = 1 + max(leftdepth, rightdepth);return depth;}int maxDepth(TreeNode* root) {return count_depth(root);}

本题当然也可以使用前序,代码如下:(充分表现出求深度回溯的过程)。由全局变量result记录遍历到的最大深度,最后返回该result

    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;}

4.二叉树的最小深度

(题目链接)
这与求最大深度有点不同,最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点,叶子节点是没有子节点的节点。
在这里插入图片描述

递归法

    int getDepth(TreeNode* node) {if (node == NULL) return 0;int leftDepth = getDepth(node->left);           // 左int rightDepth = getDepth(node->right);         // 右// 中// 当一个左子树为空,右不为空,这时并不是最低点if (node->left == NULL && node->right != NULL) { return 1 + rightDepth;}   // 当一个右子树为空,左不为空,这时并不是最低点if (node->left != NULL && node->right == NULL) { return 1 + leftDepth;}int result = 1 + min(leftDepth, rightDepth);return result;}

因此需要在求最大深度的代码种,除了将max改为min,还需要加入两个if的判断,以返回其单子节点的情况。

层序遍历-迭代法
层序遍历,则较为简单,只要遍历到该节点的左右子节点为空时,返回此时的深度即可。


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

题目链接
对于普通的二叉树
递归法
求左子节点的数量,求右子节点的数量,最后把两者取和+1,就得到该节点(包含自己)的节点数量。

    int getNodesNum(TreeNode* cur) {if (cur == NULL) return 0;int leftNum = getNodesNum(cur->left);      // 左int rightNum = getNodesNum(cur->right);    // 右int treeNum = leftNum + rightNum + 1;      // 中return treeNum;}

此时时间复杂度是:O(n),空间复杂度是O(log n)。
对于深度优先,广度优先-迭代法,只要在遍历到的节点时,令变量result+1即可。

对于完全二叉树-特殊性质
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。并且完全二叉树只有两种情况,情况一:就是满二叉树情况二:最后一层叶子节点没有满

  1. 如何计算子节点的数量?可以通过判断其子树是不是满二叉树,如果是则利用==公式(2^depth-1)==计算这个子树(满二叉树)的节点数量,如果不是则继续递归
  2. 如何判断是否是满二叉树?在完全二叉树的条件下,若向左遍历深度等于向右遍历深度,则该二叉树是满二叉树。
    int countNodes(TreeNode* root) {// 终止条件写法if (root == nullptr) return 0;TreeNode* left = root->left;TreeNode* right = root->right;int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便while (left) {  // 求左子树深度left = left->left;leftDepth++;}while (right) { // 求右子树深度right = right->right;rightDepth++;}// 满二叉树的条件if (leftDepth == rightDepth) {return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0}// 单层递归逻辑-可以看出使用了后序遍历int leftTreeNum = countNodes(root->left);       // 左int rightTreeNum = countNodes(root->right);     // 右int result = leftTreeNum + rightTreeNum + 1;    // 中return result;}

此时时间复杂度是:O(log n × log n),空间复杂度是O(log n)。


6.平衡二叉树



给定一个二叉树,判断它是否是高度平衡的二叉树:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。
虽然已经提及好几次,但要注意高度和深度的区别。在维基上以边为一度,但leetcode上以节点为一度,笔记以leetcode为准。
在这里插入图片描述
使用后序遍历符合求解二叉树节点的高度相关的问题(另外根节点的高度就是这棵树的最大深度)。而前序遍历是求二叉树节点深度相关的问题。终止条件依然是节点是否为空;单层递归逻辑是,分别求左右子节点的高度,并判断两者高度差,若高度差小于等于1,返回该节点的最大高度若高度差大于1则返回其高度为-1(正常而言节点的高度>0),这样就会使上层的节点高度返回值均大于1,最后返回-1,以-1为标志位。
递归法

    int geteHeight(TreeNode* root){// 终止条件if(root==nullptr) return 0;// 单层递归逻辑int leftH = geteHeight(root->left);if(leftH==-1) return -1;int rihgtH = geteHeight(root->right);if(rihgtH==-1) return -1;return abs(leftH-rihgtH)>1? -1:1+max(leftH, rihgtH);}bool isBalanced(TreeNode* root) {return geteHeight(root) == -1? false : true;}

迭代法
需要预拟定一个函数-求节点高度(后序遍历);然后在前序遍历中,找每一个节点的高度,对比左右子节点的高度差是否满足条件即可。


7.二叉树的所有路径

题目链接
给定一个二叉树,返回所有从根节点到叶子节点的路径。因此这类似根节点高度的求法,只是终止条件改变为了该节点的左右子节点是否为空,题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
递归法
在使用递归法的时候,切记回溯和递归是一一对应的,有一个递归,就要有一个回溯

    void traversal(TreeNode* root, std::vector<int>& path, std::vector<std::string>& result){path.push_back(root->val); //path添加当前节点值if(root->left==nullptr && root->right==nullptr){std::string sPath;for(int i=0; i<path.size()-1; i++){sPath += to_string(path[i]);sPath += "->";}sPath += to_string(path[path.size()-1]);result.push_back(sPath);return;}// 单层递归逻辑if(root->left){traversal(root->left, path, result);path.pop_back(); //回溯}if(root->right){traversal(root->right, path, result);path.pop_back(); //回溯}}vector<string> binaryTreePaths(TreeNode* root) {std::vector<std::string> result;std::vector<int> path;if(root==nullptr) return result;traversal(root, path, result);return result;}

8.左叶子之和

题目链接
这道题目要求左叶子之和,其实是比较绕的,因为不能判断本节点是不是左叶子节点,需要通过通过节点的父节点判断本节点的属性。因此需使用后序遍历的方法。
递归法

    int sumOfLeftLeaves(TreeNode* root) {if(root == nullptr) return 0;if(root->left==nullptr && root->right==nullptr) return 0;int leftvalue = sumOfLeftLeaves(root->left);int rightval = sumOfLeftLeaves(root->right);if(root->left && !root->left->left && !root->left->right){leftvalue = root->left->val;}int sum = leftvalue + rightval;return sum;}

迭代法

    int sumOfLeftLeaves(TreeNode* root) {stack<TreeNode*> st;if (root == NULL) return 0;st.push(root);int result = 0;while (!st.empty()) {TreeNode* node = st.top();st.pop();if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {result += node->left->val;}if (node->right) st.push(node->right);if (node->left) st.push(node->left);}return result;}

总结

  1. 递归函数什么时候需要返回值?什么时候不需要返回值?以下总结三点:

    • 如果需要搜索二叉树且不用处理递归返回值,递归函数就不要返回值
    • 如果需要搜索二叉树且需要处理递归返回值,递归函数就需要返回值。
    • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径就要及时返回。
  2. 思考如何根据两个顺序构造唯一一个二叉树

    • 后序数组的最后一个元素为切割点,先切中序数组,根据所切割的左中序数组大小作为切割点,反过来再切后序数组
    • 一层一层地切下去,每次后序数组最后一个元素就是节点元素
      因为后序数组的最后一个元素必为最深的父节点,而中序数组中相邻的元素会被父节点所切割
      在这里插入图片描述
  3. 构造树一般采用前序遍历,因为先构造中间节点,再构造左,右子节点。

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

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

相关文章

stm32学习笔记---TIM输出比较(理论部分)

目录 TIM简介 定时器类型 基本定时器的结构图 时基单元 预分频器 计数器 自动重装寄存器 主模式触发DAC的功能 通用定时器的结构图 计数器的计数模式 内外时钟源选择和主从触发模式的结构 外部时钟模式2 外部时钟模式1 其他部分 输出比较电路 输入捕获电路 高…

深度测试中的隐藏面消除技术

by STANCH 标签&#xff1a;#计算机图形学 #深度测试 #深度测试 #隐藏面消除 1.概述 根据我们的日常经验&#xff0c;近处的物体会挡住后面的物体&#xff0c;在三维场景中通常通过深度缓冲来实现这样的效果。深度缓冲记录着屏幕对应的每个像素的深度值。模型一开始所在的局部…

我对ChatGPT-5的期待

在科技飞速发展的今天&#xff0c;人工智能&#xff08;AI&#xff09;已经成为我们生活中不可或缺的一部分。尤其是近年来&#xff0c;随着ChatGPT等先进AI模型的推出&#xff0c;我们见证了AI技术在智能水平上的巨大飞跃。作为这一领域的最新成果&#xff0c;GPT-5的即将发布…

2005年下半年软件设计师【上午题】试题及答案

文章目录 2005年下半年软件设计师上午题--试题2005年下半年软件设计师上午题--答案 2005年下半年软件设计师上午题–试题 2005年下半年软件设计师上午题–答案

解决ssh: connect to host IP port 22: Connection timed out报错(scp传文件指定端口)

错误消息 ssh: connect to host IP port 22: Connection timed out 指出 SSH 客户端尝试连接到指定的 IP 地址和端口号&#xff08;默认 SSH 端口是 22&#xff09;&#xff0c;但是连接超时了。这意味着客户端没有在预定时间内收到来自服务器的响应。 可能的原因 SSH 服务未…

C语言---C指针+ASCII码

内存地址&#xff1a;内存中每个字节单位都有一个编号&#xff08;一般用十六进制表示&#xff09; 存储类型 数据类型 *指针变量名&#xff1b;int *p; //定义了一个指针变量p,指向的数据是int类型的。访问指针所指向空间的内容用取内容运算符* &&#xff1a;取地址符&am…

LeetCode —— 只出现一次的数字

只出现一次的数字 I 本题依靠异或运算符的特性&#xff0c;两个相同数据异或等于0&#xff0c;数字与0异或为本身即可解答。代码如下: class Solution { public:int singleNumber(vector<int>& nums) {int ret 0;for (auto e : nums){ret ^ e;}return ret;} };只出…

数据模型(models)

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 &#xff08;1&#xff09;在App中添加数据模型 在app1的models.py中添加如下代码&#xff1a; from django.db import models # 引入django.…

Qt开发 | Qt界面布局 | 水平布局 | 竖直布局 | 栅格布局 | 分裂器布局 | setLayout使用 | 添加右键菜单 | 布局切换与布局删除重构

文章目录 一、Qt界面布局二、Qt水平布局--QHBoxLayout三、Qt竖直布局四、Qt栅格布局五、分裂器布局代码实现六、setLayout使用说明七、布局切换与布局删除重构1.如何添加右键菜单2.布局切换与布局删除重构 一、Qt界面布局 Qt的界面布局类型可分为如下几种 水平布局&#xff08;…

谐波减速器行业发展速度有望加快 工业机器人领域为其最大需求端

谐波减速器行业发展速度有望加快 工业机器人领域为其最大需求端 谐波减速器指通过增大转矩、降低转速等方式实现减速目的的精密传动装置。谐波减速器具有轻量化、体积小、承载能力大、精度高、可靠性高、运行噪音小等优势&#xff0c;广泛应用于工业机器人、半导体制造、精密医…

AWS中国云配置强制MFA策略后导致AWS CLI和IDEA中无法使用问题

问题 之前的文章《AWS中国IAM用户强制使用MFA》&#xff0c;启用必须使用MFA策略才能使用AWS服务。但是&#xff0c;开启之后&#xff0c;遇到了本地开发环境的IDEA和AWS CLI不能正常调用ssm的配置中心问题。 解决思路 在本地配置文件中&#xff0c;配置使用能够正常使用ssm…

web开发前后端分离

文章目录 1.广义上的前后端分离 1.广义上的前后端分离 优点&#xff1a; 1.前后端分离&#xff0c;便于后期维护;2.前端服务器只需要返回静态界面&#xff0c;后端服务器只提供增删查改的数据返回&#xff0c;把数据的转换逻辑的处理压力转移到了客户端;

MySQL 8版本的新功能和改进有哪些?(MySQL收藏版)

目录 1. 简单介绍 2. 发展历史 3. MySQL 8产品特性 4. 数据库性能重点分析 1. 原生 JSON 支持改进 2. 隐式列优化 3. 改进的查询优化器 4. 并行查询 5. 分区表改进 MySQL 是一个流行的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;由瑞典公司 M…

了解SD-WAN与传统WAN的区别

近年来&#xff0c;许多企业选择了SD-WAN作为他们的网络解决方案。云基础架构的SD-WAN不仅具备成本效益&#xff0c;而且提供更安全、更可靠的WAN连接&#xff0c;有助于实现持续盈利。客户能够更好地控制他们的网络&#xff0c;个性化定制且无需额外成本。 那么&#xff0c;为…

服务器数据恢复—raid故障导致部分分区无法识别/不可用的数据恢复案例

服务器数据恢复环境&#xff1a; 一台某品牌DL380服务器中3块SAS硬盘组建了一组raid。 服务器故障&#xff1a; RAID中多块磁盘出现故障离线导致RAID瘫痪&#xff0c;其中一块硬盘状态指示灯显示红色。服务器上运行的数据库在D分区&#xff0c;备份文件存放在E分区。由于RAID瘫…

[信号与系统]模拟域中的一阶低通滤波器和二阶滤波器

前言 不是学电子出身的&#xff0c;这里很多东西是问了朋友… 模拟域中的一阶低通滤波器传递函数 模拟域中的一阶低通滤波器的传递函数可以表示为&#xff1a; H ( s ) 1 s ω c H(s) \frac{1}{s \omega_c} H(s)sωc​1​ 这是因为一阶低通滤波器的设计目标是允许低频信…

05-java基础——循环习题

循环的选择&#xff1a;知道循环的次数或者知道循环的范围就使用for循环&#xff0c;其次再使用while循环 猜数字 程序自动生成一个1-100之间的随机数&#xff0c;在代码中使用键盘录入去猜出这个数字是多少&#xff1f; 要求&#xff1a;使用循环猜&#xff0c;一直猜中为止…

时序预测 | Matlab基于CNN-BiLSTM-Attention多变量时间序列多步预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于CNN-BiLSTM-Attention多变量时间序列多步预测&#xff1b; 2.多变量时间序列数据集&#xff08;负荷数据集&#xff09;&#xff0c;采用前96个时刻预测的特征和负荷数据预测未来96个时刻的负荷数据&…

品牌为什么需要3D营销?

在对比传统品牌营销手段时&#xff0c;线上3D互动营销以其更为生动的展示效果脱颖而出。它通过构建虚拟仿真场景&#xff0c;创造出一个身临其境的三维空间&#xff0c;充分满足了客户对实体质感空间的期待。不仅如此&#xff0c;线上3D互动营销还能实现全天候24小时无间断服务…

PyTorch中“No module named ‘torch._six‘“的报错场景及处理方法

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 在使用PyTorch时&#xff0c;您可能会遇到"No module named ‘torch._six’"的错误。这通常是因为PyTorch的某些…