LeetCode 热题 100 | 二叉树(四)

目录

1  114. 二叉树展开为链表

2  105. 从前序与中序遍历序列构造二叉树

3  437. 路径总和 III


菜鸟做题(即将返校版),语言是 C++

1  114. 二叉树展开为链表

题眼:展开后的单链表应该与二叉树 先序遍历 顺序相同。

而先序遍历就是指,先遍历左子树,再遍历右子树。题意所说的展开,无非就是让左子树插队到右子树的前面去。

解题思路:采用递归,对于每个节点,先将它的左子树链到右侧去,再让右子树链到左子树的后面。

思路说明图:

对于节点 “1”,绿色部分为其左子树,黄色部分为其右子树。我们需要做的就是:先将左子树链到 “1” 的右侧去,再让右子树链到左子树的后面。对于节点 “2”,同理。

class Solution {
public:void flatten(TreeNode* root) {if (!root) return;TreeNode * p = root->left;if (p) {while (p->right) {p = p->right;}TreeNode * q = root->right;root->right = root->left;root->left = nullptr; // heap-use-after-free on address 0x503000000138p->right = q;}flatten(root->right);}
};

说明:为了能使右子树链到左子树的屁股后面,我们需要找到左子树的屁股,代码如下。

while (p->right) {p = p->right;
}

坑点:题目中说 “左子指针始终为 null”,即移走左子树后,要把左子指针置为空指针,代码如下。

root->left = nullptr; // heap-use-after-free on address 0x503000000138

否则会出现注释中的报错。

2  105. 从前序与中序遍历序列构造二叉树

遍历特点:

  • 前序:根节点 << 左子树 << 右子树
  • 中序:左子树 << 根节点 << 右子树

因此,preorder 和 inorder 数组中的元素也呈现出上述排列规律。

思路说明图:

对于 preorder 数组,根据遍历特点,显然最左侧的 “3” 是根节点(root)。但是,我们无法根据 preorder 数组得知 “3” 的左子树和右子树分别是哪两坨。这时,inorder 数组就派上用场了。我们在 inorder 数组中定位 “3”,根据遍历特点,“3” 的左侧部分就是左子树(“9”),右侧部分就是右子树(“20” “15” “7”)。

现在,我们知道 “3” 的左子树和右子树分别是什么了,以及它们各自的长度。由此,我们可以在 preorder 数组中得到 “3” 的左子树和右子树所处的区间,即上图中的 绿色 部分和 蓝色 部分。又根据遍历特点,我们知道 绿色 部分的最左侧是左子树的根节点(root->left)蓝色 部分的最左侧是右子树的根节点(root->right)

以此类推,构造整个二叉树。

综上,preorder 数组的作用是定位各个根节点,inorder 数组的作用是定位左子树和右子树。

参数说明:

  • pre_left、pre_right:当前子树在 preorder 数组所处的区间
  • in_left、in_right:当前子树在 inorder 数组所处的区间
  • sub_ltree:左子树长度,帮助定位左子树区间
class Solution {
public:vector<int> preorder, inorder;unordered_map<int, int> hash;TreeNode * helper(int pre_left, int pre_right, int in_left, int in_right) {if (pre_left > pre_right) return nullptr;int pre_root = pre_left;int in_root = hash[preorder[pre_root]];int sub_ltree = in_root - in_left;TreeNode * node = new TreeNode(preorder[pre_root]);node->left = helper(pre_left + 1, pre_left + sub_ltree,in_left, in_root - 1);node->right = helper(pre_left + sub_ltree + 1, pre_right,in_root + 1, in_right);return node;}TreeNode* buildTree(vector<int>& arr1, vector<int>& arr2) {int n = arr1.size();preorder = arr1;inorder = arr2;for (int i = 0; i < n; ++i) {hash[inorder[i]] = i;}return helper(0, n - 1, 0, n - 1);}
};

3  437. 路径总和 III

关键字:深度搜索(先序遍历)+ 前缀和

我百度了一下,说它俩是一个意思。我觉得采用先序遍历是因为,它每次都是沿着一条直线去遍历的,而不是像中序遍历那样跳着遍历的。因此,先序遍历更符合题意。

思路说明图:路径 “5, 3” 的和可以看作是路径 “10, 5, 3” 的和减去路径 “10” 的和,也就是可以转换为字符串那一节的前缀和问题。

这里说的前缀是指以根节点为开始的路径,而前缀和就是各个前缀的路径和。

具体解法:

① 定义变量

  • hash 用于存放各个前缀的路径和
  • total 用于记录当前加总出的路径和
  • count 用于记录符合条件的路径和的个数

② 查询符合条件的路径和

if (hash.count(total - targetSum)) {count = hash[total - targetSum];
}

就是在 hash 表中寻找,是否有前缀的路径和与当前路径和的差为 targetSum 值。

③ 先序遍历

hash[total]++;
count += helper(root->left, total, targetSum);
count += helper(root->right, total, targetSum);
hash[total]--;

在走向下一个节点之前,先把当前路径和存入 hash 表中,因为它是前缀和。同时,当基于这条路径的所有路径都被遍历完毕后,要把这条路径的路径和移出 hash 表,即 hash[total]-- 。

比如,我们遍历完了节点 “3” 及其左右子树,接下来就该遍历 “5” 的右子树了。而 “5” 的右子树不会经过路径 “10, 5, 3” 。因此,在节点 “3” 发现自己的左右子树被遍历完时,“3” 就应该回收以自己为终点的路径 “10, 5, 3” 了。

class Solution {
public:unordered_map<long, int> hash;int helper(TreeNode * root, long total, int targetSum) {if (!root) return 0;int count = 0;total += root->val;if (hash.count(total - targetSum)) {count = hash[total - targetSum];}hash[total]++;count += helper(root->left, total, targetSum);count += helper(root->right, total, targetSum);hash[total]--;return count;}int pathSum(TreeNode* root, int targetSum) {hash[0] = 1;return helper(root, 0, targetSum);}
};

说明:下述代码是为了处理前缀本身的路径和等于 targetSum 的情况,也就是它(被减数)不需要和另一个前缀(减数)做减法。这里用路径和为 0 来表示减数,否则这种情况会被忽略。

hash[0] = 1;

题外话:虽然 count 变量在每一次递归中都是重新定义的,但是因为它是返回值,所以还是能起到计数器的作用。而 total 变量是作为参数一层一层传下去的,所以它也能起到计数器的作用。

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

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

相关文章

MBG(Mybatis-Generator)生成代码

目录 步骤&#xff1a; 1. 创建数据库表 2. 配置 MyBatis Generator 3. 运行 MyBatis Generator 4. 编写业务逻辑 在实际开发中&#xff0c;你会发现有很多重复的工作&#xff1a; 首先是PO对象&#xff0c;我们往往创建与数据库表字段一一对应的PO对象; 其次在Mapper里…

【电机仿真】HFI算法脉振高频电压信号注入观测器-PMSM无感FOC控制

【电机仿真】HFI算法脉振高频电压信号注入观测器-PMSM无感FOC控制 文章目录 前言一、脉振高频电压注入法简介&#xff08;注入在旋转坐标系的d轴&#xff09;1.旋转高频电压&#xff08;电流&#xff09;注入法2.脉振高频电压注入法 二、高频注入理论1.永磁同步电机的高频模型2…

java spring 01 IOC源码

01.spring 中的基础是IOC 中有一个方法 例子&#xff1a; 01. 02. 03. 这里是扩展方法&#xff0c;现在是空的 beanfactorypostprocessors&#xff1a; 国际化&#xff1a;&#xff08;一般不管&#xff09; 广播器: 监听器&#xff1a; 实例化&#xff1…

Day03-课后练习(流程控制_分支结构)(判断年、月、日是否合法,判断打鱼还是晒网,判断星座)

参考答案博客链接跳转 文章目录 巩固题1、从键盘输入一个整数&#xff0c;判断它是否是5的倍数2、从键盘输入一个字符&#xff0c;判断字符类型3、计算折扣后金额4、输出月份对应的英语单词5、计算今天是星期几 简答题拔高题&#xff08;自愿&#xff09;6、判断年、月、日是否…

使用Node.js和Vue.js构建全栈Web应用

随着互联网的迅速发展&#xff0c;Web应用程序的开发变得越来越复杂和多样化。为了满足用户不断变化的需求&#xff0c;全栈开发已成为一个备受关注的话题。在本篇博客中&#xff0c;我将介绍如何使用Node.js和Vue.js来构建全栈Web应用。 Node.js是一个基于Chrome V8引擎的Jav…

Vue.js+SpringBoot开发大学兼职教师管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容三、界面展示3.1 登录注册3.2 学生教师管理3.3 课程管理模块3.4 授课管理模块3.5 课程考勤模块3.6 课程评价模块3.7 课程成绩模块3.8 可视化图表 四、免责说明 一、摘要 1.1 项目介绍 大学兼职教师管理系统&#xff0c;旨…

TongWEB(东方通),部署WEB前后端项目步骤

我的系统: 银河麒麟桌面系统V10(SP1)(兆芯) 环境需要搭建好,什么redis,数据库等 1.准备项目前端war包 (我后端项目本就是war部署,jar转war自行百度一下吧) 进入前端打包好的dist文件夹,创建一个文件夹 WEB-INF ,再在 WEB-INF 里创建一个 web.xml 文件,文件内容: <web-…

小家电—简易过零检测电路

趁刚开工时间有空&#xff0c;总结分析下&#xff0c;在工作项目中常用过零检测电路。 图一 图二 图一在项目中较为常用&#xff0c;两个电路都是通过钳位二极管限幅产生过零脉冲信号。 过零信号高电平被钳位在5.7V&#xff0c;低电平为-0.7V 高电平&#xff1a;VCC0.7V 低电…

猜测了一个sora模型结构

如果是上述的这种结构&#xff0c;可以确定的是patch 的size &#xff08;一个图像的小片&#xff09;是固定大小的 那么调节一个视觉分辨率大小通过patchs的大小决定。 如图所示可以证明输入的时候图片没有本物理人为的分割为小片&#xff0c;是通过一个模型进行分割为 小片。…

如何通过信息化系统降低连锁品牌企业的财务成本

最近身边几个做连锁品牌的朋友问&#xff0c;能不能通过信息化系统降低连锁管理门店的财务成本&#xff0c;让整体的运营合法合规&#xff0c;降低税收成本。今天商淘云和大家分享如何通过信息化系统降低连锁管理门店的财务成本。 传统的连锁门店是大家自动核对账目&#xff0c…

前后端项目宝塔linux部署(springboot,vue,python)

宝塔linux安装就省略了&#xff0c;网上一堆 1.部署后端 1.首先把自己项目里面打包好的的jar包上传到服务器随便一个地方&#xff0c;我这里就上传到www/wwwroot下面了&#xff0c;宝塔的文件页面可以很便携上传 2.然后到下面这个页面 选那个java环境管理装个jdk&#xff…

双向循环链表防断裂下的指针指向

已知有一个带有表头结点的双向循环链表L&#xff0c;结点结构为 prevdatanext 其中&#xff0c;prev和next分别是指向其直接前驱和直接后继结点的指针。现要删除指针p所指的结点&#xff0c;正确的语句序列是&#xff08; &#xff09;。 A.p->next->prevp->…