Leetcode算法训练日记 | day18

一、找树左下角的值

1.题目

Leetcode:第 513 题

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

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

示例 2:

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

2.解题思路

使用队列来实现层序遍历,每次从队列中取出所有节点,并检查每个节点的子节点,将它们加入队列中以便后续遍历。在每一层中,第一个被取出的节点(即队列中的第一个节点)将是该层的最左侧节点,因此将其值设置为结果。当遍历结束时,返回结果变量,它包含了二叉树最底层的左侧值。

3.实现代码

#include <iostream>
#include <vector>
#include <queue>
using namespace std;// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {int val; // 存储节点的值。TreeNode* left; // 指向该节点左子树的指针。TreeNode* right; // 指向该节点右子树的指针。// TreeNode的构造函数,用于创建一个TreeNode实例。// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};// 一、找树左下角的值(递归法)
class Solution1 {
public:int maxDepth = INT_MIN;// 初始化最大深度为最小整数值,用于记录遍历过程中的最大深度。int result; // 初始化结果变量,用于存储最底层左侧的值。// traversal函数用于递归遍历二叉树,并找到最底层左侧的值。void traversal(TreeNode* root, int depth) {// 如果当前节点是叶子节点(没有左右子节点),检查当前深度是否大于已知的最大深度。if (root->left == NULL && root->right == NULL) {if (depth > maxDepth) { // 如果当前深度大于最大深度,则更新最大深度,并记录当前节点的值作为结果。maxDepth = depth;result = root->val;}return;// 到达叶子节点后,返回不再继续遍历。}if (root->left) {// 如果当前节点有左子节点,递归遍历左子树,并在遍历完后减去深度。depth++;traversal(root->left, depth);depth--;}if (root->right) {// 如果当前节点有右子节点,递归遍历右子树,并在遍历完后减去深度。depth++;traversal(root->right, depth);depth--;}return;// 遍历过程中返回,以便继续遍历其他分支。}// findBottomLeftValue函数是公共成员函数,用于返回二叉树最底层左侧的值。int findBottomLeftValue(TreeNode* root) {traversal(root, 0);// 调用递归遍历函数,遍历二叉树。return result; // 返回记录的最底层左侧的值。}
};// 二、找树左下角的值(迭代法)
class Solution2 {
public:// findBottomLeftValue函数用于找到并返回二叉树最底层的左侧值。int findBottomLeftValue(TreeNode* root) {queue<TreeNode*> que; // 创建一个队列que,用于存储待遍历的节点。if (root != NULL) que.push(root);  // 如果根节点不为空,将其入队。int result = 0; // 初始化结果变量为0。// 使用while循环遍历队列不为空时的所有节点。while (!que.empty()) {int size = que.size();// 获取队列中的节点数量。for (int i = 0; i < size; i++) { // 遍历当前队列中的所有节点。TreeNode* node = que.front(); // 取出队列前端的节点。que.pop(); // 将节点从队列中移除。if (i == 0) {   // 如果是当前层的第一个节点,将其值设为结果。result = node->val;}if (node->left) que.push(node->left);// 如果节点有左子节点,将其入队。if (node->right) que.push(node->right); // 如果节点有右子节点,将其入队。}}return result;// 返回记录的最底层左侧的值。}
};//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {return new TreeNode(value);
}// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {if (values.empty()) return NULL;TreeNode* root = createNode(values[0]);queue<TreeNode*> queueNode;queueNode.push(root);int i = 1;while (!queueNode.empty()) {TreeNode* node = queueNode.front();queueNode.pop();if (i < values.size()) {node->left = createNode(values[i++]);queueNode.push(node->left);}if (i < values.size()) {node->right = createNode(values[i++]);queueNode.push(node->right);}}return root;
}// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {for (int value : vec) {cout << value << " ";}cout << endl;
}// 主函数
int main() {vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树TreeNode* root = buildTree(treeValues); // 构建二叉树Solution1 s1;// 创建Solution类的实例Solution2 s2;int result1 = s1.findBottomLeftValue(root);// 传入二叉树的根节点int result2 = s2.findBottomLeftValue(root);cout << "二叉树的左下角的值(递归法)是: " << result1 << endl;cout << endl;cout << "二叉树的左下角的值(迭代法)是: " << result2 << endl;cout << endl;return 0;
}

二、路径总和

1.题目

Leetcode:第 112 题

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

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

示例 1:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径
2.解题思路

使用递归法和迭代法遍历二叉树节点,使用一个栈来存储每个节点及其对应的路径数值。在遍历过程中,如果遇到叶子节点且其路径数值等于目标和,返回true;否则继续遍历左右子节点。如果遍历结束仍未找到满足条件的路径,返回false。

3.实现代码
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {int val; // 存储节点的值。TreeNode* left; // 指向该节点左子树的指针。TreeNode* right; // 指向该节点右子树的指针。// TreeNode的构造函数,用于创建一个TreeNode实例。// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};// 一、二叉树的路径总和(递归法)
class Solution1 {
public:// traversal是一个辅助函数,用于递归遍历二叉树,检查是否存在路径和等于count的路径。// node是当前遍历到的节点,count是当前的路径和与目标和的差值。bool traversal(TreeNode* node, int count) {// 如果当前节点是叶子节点,并且count不等于0,说明路径和不等于目标和,返回false。if (node->left == NULL && node->right == NULL && count != 0) return false;// 如果当前节点是叶子节点,并且count等于0,说明路径和等于目标和,返回true。if (node->left == NULL && node->right == NULL && count == 0) return true;// 如果当前节点有左子节点,递归遍历左子树。if (node->left) {count -= node->left->val; // 从当前路径和中减去左子节点的值,因为左子节点的值是路径和的一部分。if (traversal(node->left, count)) return true;  // 如果在左子树中找到满足条件的路径,返回true。count += node->left->val;  // 回溯,将左子节点的值加回到路径和中。}// 如果当前节点有右子节点,递归遍历右子树。if (node->right) {count -= node->right->val; // 从当前路径和中减去右子节点的值,因为右子节点的值是路径和的一部分。if (traversal(node->right, count)) return true;// 如果在右子树中找到满足条件的路径,返回true。count += node->right->val;  // 回溯,将右子节点的值加回到路径和中。}return false;// 如果遍历完左右子树都没有找到满足条件的路径,返回false。}// hasPathSum是一个成员函数,用于判断是否存在从根节点到叶子节点的路径和等于目标和的路径。// root是二叉树的根节点,targetSum是目标和。bool hasPathSum(TreeNode* root, int targetSum) {if (root == NULL) return false;// 如果根节点为空,返回false,因为不存在路径。// 调用辅助函数traversal,从根节点开始遍历,初始路径和为targetSum - 根节点的值。return traversal(root, targetSum - root->val);}
};// 二、二叉树的路径总和(迭代法)
class Solution2 {
public:// haspathsum函数用于判断是否存在路径和等于sum的路径。// root是二叉树的根节点,sum是目标和。bool haspathsum(TreeNode* root, int sum) {if (root == NULL) return false;// 如果根节点为空,返回false,因为不存在路径。stack<pair<TreeNode*, int>> st;// 创建一个栈st,用于存储节点指针和对应的路径数值。st.push(pair<TreeNode*, int>(root, root->val));// 将根节点及其路径数值(根节点的值)入栈。// 使用while循环遍历栈不为空时的所有元素。while (!st.empty()) {pair<TreeNode*, int> node = st.top();// 取出栈顶元素,包含节点指针和路径数值。st.pop();// 如果当前节点是叶子节点,并且路径数值等于sum,返回true。if (!node.first->left && !node.first->right && sum == node.second) return true;// 如果当前节点有右子节点,将其及其路径数值(当前路径数值加上右子节点的值)入栈。if (node.first->right) {st.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));}// 如果当前节点有左子节点,将其及其路径数值(当前路径数值加上左子节点的值)入栈。if (node.first->left) {st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));}}return false; // 如果遍历完所有节点都没有找到满足条件的路径,返回false。}
};//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {return new TreeNode(value);
}// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {if (values.empty()) return NULL;TreeNode* root = createNode(values[0]);queue<TreeNode*> queueNode;queueNode.push(root);int i = 1;while (!queueNode.empty()) {TreeNode* node = queueNode.front();queueNode.pop();if (i < values.size()) {node->left = createNode(values[i++]);queueNode.push(node->left);}if (i < values.size()) {node->right = createNode(values[i++]);queueNode.push(node->right);}}return root;
}// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {for (int value : vec) {cout << value << " ";}cout << endl;
}// 主函数
int main() {vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树TreeNode* root = buildTree(treeValues); // 构建二叉树Solution1 s1;// 创建Solution类的实例Solution2 s2;int targetSum = 11;int result1 = s1.hasPathSum(root, targetSum);// 传入二叉树的根节点int result2 = s2.haspathsum(root, targetSum);cout << "targetSum = " << targetSum << endl;cout << endl;cout << "判断二叉树的路径总和(递归法)结果是: " << result1 << endl;cout << endl;cout << "判断二叉树的路径总和(迭代法)结果是:" << result2 << endl;cout << endl;return 0;
}

三、从中序与后序遍历序列构造二叉树

1.题目

Leetcode:第 106 题

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]
2.解题思路

创建traversal函数递归地根据中序和后序遍历序列重建二叉树。首先确定根节点,然后根据根节点在中序遍历序列中的位置分割序列,得到左右子树的遍历序列。接着递归地重建左右子树,并将它们分别作为根节点的左右子节点。buildTree函数是重建二叉树的入口点,它调用traversal函数并传入中序和后序遍历序列。

3.实现代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {int val; // 存储节点的值。TreeNode* left; // 指向该节点左子树的指针。TreeNode* right; // 指向该节点右子树的指针。// TreeNode的构造函数,用于创建一个TreeNode实例。// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};// 从中序与后序遍历序列构造二叉树
class Solution {
private:// traversal函数是一个辅助函数,用于递归地重建二叉树。TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {if (postorder.size() == 0) return NULL;// 如果后序遍历的序列为空,说明没有节点,返回空指针。int rootValue = postorder[postorder.size() - 1];// 获取后序遍历序列中的最后一个元素,即根节点的值。TreeNode* root = new TreeNode(rootValue);// 创建一个新的根节点。if (postorder.size() == 1) return root;// 如果后序遍历序列只有一个元素,说明是叶子节点,直接返回根节点。// 查找中序遍历序列中根节点的位置,用于分割左右子树。int delimiterIndex;for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {if (inorder[delimiterIndex] == rootValue) break;}// 根据根节点的位置分割中序遍历序列为左右子树的中序遍历序列。vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());postorder.resize(postorder.size() - 1);// 移除后序遍历序列中的根节点,为后续递归调用做准备。// 根据根节点的位置分割后序遍历序列为左右子树的后序遍历序列。vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());root->left = traversal(leftInorder, leftPostorder);// 递归地重建左子树,并将其作为根节点的左子节点。root->right = traversal(rightInorder, rightPostorder); // 递归地重建右子树,并将其作为根节点的右子节点。return root;// 返回重建的根节点。}
public:// buildTree函数是公共成员函数,用于根据给定的中序和后序遍历序列重建二叉树。TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {if (inorder.size() == 0 || postorder.size() == 0) return NULL; // 如果中序或后序遍历序列为空,返回空指针。return traversal(inorder, postorder);// 调用辅助函数开始重建二叉树。}
};//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {return new TreeNode(value);
}// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {if (values.empty()) return NULL;TreeNode* root = createNode(values[0]);queue<TreeNode*> queueNode;queueNode.push(root);int i = 1;while (!queueNode.empty()) {TreeNode* node = queueNode.front();queueNode.pop();if (i < values.size()) {node->left = createNode(values[i++]);queueNode.push(node->left);}if (i < values.size()) {node->right = createNode(values[i++]);queueNode.push(node->right);}}return root;
}// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {for (int value : vec) {cout << value << " ";}cout << endl;
}
int main()
{Solution s;vector<int> inorder = { 9,3,15,20,7 };vector<int> postorder = { 9,15,7,20,3 };TreeNode* result=s.buildTree(inorder, postorder);cout << "从中序与后序遍历序列构造二叉树:" << endl;cout <<"root = "<<result->val << endl;cout << "root->left = " << result->left->val << endl;cout << "root->right = " << result->right->val << endl;cout << "root->right->left = " << result->right->left->val << endl;cout << "root->right->right = " << result->right->right->val << endl;return 0;
}

 ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。

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

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

相关文章

Python:六大数据类型理论与示例

在这篇文章中&#xff0c;我们深入探讨了Python中的六大基本数据类型&#xff1a;数字&#xff08;Number&#xff09;、字符串&#xff08;String&#xff09;、列表&#xff08;List&#xff09;、元组&#xff08;Tuple&#xff09;、集合&#xff08;Set&#xff09;、字典…

Hive的分区与排序

一、Hive分区 1.引入&#xff1a; 在大数据中&#xff0c;最常见的一种思想就是分治&#xff0c;我们可以把大的文件切割划分成一个个的小的文件&#xff0c;这样每次操作一个个小的文件就会很容易了&#xff0c;同样的道理&#xff0c;在hive当中也是支持这种思想的&#xff…

UE4 避免布料模拟重置后抖动

问题&#xff1a;每次设置带布料模拟的布料新位置&#xff0c;就会发生突然的抖动 解决办法&#xff1a;给“布料混合权重”或“布料最大距离缩放”K帧&#xff0c;参考数值为0.2—1&#xff08;红框内的值都试过无法解决&#xff09;

JVM性能调优——运行时参数

文章目录 1、JVM参数选项类型1.1、标准参数选项1.2、非标准参数选项1.3、非稳定参数选项 2、添加JVM参数的方式3、常用JVM参数选项4、通过Java代码获取JVM参数5、小结 熟悉JVM参数对于系统调优是非常重要的。比如一个高流量的延迟的电子交易平台&#xff0c;它要求的响应时间都…

头歌机器学习实验 第7次实验 局部加权线性回归

任务描述 本关任务&#xff1a;编写一个利用局部加权计算回归系数的小程序。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.局部加权算法的思想&#xff1b;2.局部加权的核心算法。 局部加权算法的思想 在局部加权算法中 &#xff0c;我们给待预测点附近…

【mT5多语言翻译】之六——推理:多语言翻译与第三方接口设计

请参考本系列目录&#xff1a;【mT5多语言翻译】之一——实战项目总览 [1] 模型翻译推理 在分别使用全量参数微调和PEFT微调训练完模型之后&#xff0c;我们来测试模型的翻译效果。推理代码如下&#xff1a; # 导入模型 if conf.is_peft:model AutoModelForSeq2SeqLM.from_pr…

为什么需要SOCKS代理?

在数字化时代&#x1f310;&#xff0c;随着网络安全威胁的不断演进和增加&#xff0c;保护个人隐私和数据安全成为了互联网用户的一大挑战&#x1f6e1;️。在寻求增强在线安全和隐私的解决方案时&#xff0c;SOCKS代理成为了一个关键的技术工具&#x1f511;。本文旨在详细探…

python如何输入多行

Python中的Input()函数在输入时&#xff0c;遇到回车符&#xff0c;那么一次输入就结束了。这不能满足输入多行文本并且行数也不确定的情形&#xff0c;当然输入空行也是允许的。 方法1&#xff1a;利用异常处理机制实现 lines[] while True:try:lines.append(input())except:…

JSON三种数据解析方法

文章目录 一、什么是JSON数据&#xff1f;二、JSON解析方法2.1、原生解析2.2、Gson解析2.3、FastJson解析 四、总结 一、什么是JSON数据&#xff1f; 先看下面&#xff0c;这里有一段JSON数据&#xff0c;我们根据这段数进行讲解&#xff1a; {"paramz": {"fe…

springboot+vue全栈开发【1.准备工作篇】

目录 前言环境配置关于Maven 前言 为了坚持每天写博客所以开了这个系列~~ 环境配置 1.配置java环境&#xff0c;下载jdk&#xff0c;配置环境变量 这一步有很多教程&#xff0c;不赘述了 2.安装idea 关于Maven Maven是一个项目管理工具&#xff0c;可以对java项目进行自动化…

Harmony与Android项目结构对比

主要文件对应 Android文件HarmonyOS文件清单文件AndroidManifest.xmlmodule.json5Activity/Fragmententryability下的ts文件XML布局pages下的ets文件resresourcesModule下的build.gradleModule下的build-profile.json5gradlehvigor根目录下的build.gradle根目录下的build-profi…

Linux——fork复制进程

1)shell: 在计算机科学中&#xff0c;Shell俗称壳&#xff08;用来区别于核&#xff09;&#xff0c;是指“为使用者提供操作界面”的软件&#xff08;command interpreter&#xff0c;命令解析器&#xff09;。它类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令&…

【Locust分布式压力测试】

Locust分布式压力测试 https://docs.locust.io/en/stable/running-distributed.html Distributed load generation A single process running Locust can simulate a reasonably high throughput. For a simple test plan and small payloads it can make more than a thousan…

30岁《爱·回家》小花多次得罪高层,正式宣布离巢TVB。

30岁的苏韵姿&#xff08;Andrea&#xff09;16年选港姐入行&#xff0c;虽然无三甲名次&#xff0c;但靠着皇后大学戏剧学士学位背景&#xff0c;她很快已有机会入剧组&#xff0c;凭《爱回家之开心速递》熊心如&#xff08;红衫鱼&#xff09;一角成功入屋&#xff0c;不过去…

Mongodb入门--头歌实验MongoDB 数据库基本操作

一、数据库创建 任务描述 本关任务&#xff1a;创建数据库。 相关知识 本关评测是在 Linux 环境下进行的&#xff0c;MongoDB 的安装与配置测评系统均已默认完成。 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 1.如何连接数据库&#xff1b; 2.如何创建数据库。 连接数…

双云及多云融合(混合云)

背景&#xff1a;客户对于业务的高可用需求&#xff0c;当发生故障时&#xff0c;业务还能正常使用&#xff0c;如某云机房整体宕机&#xff0c;或云管理服务整体宕掉&#xff0c;导致客户业务不可用&#xff0c;此时&#xff0c;需有业务能顺利切换到灾备云上。 需求&#xf…

[蓝桥杯 2018 国 C] 迷宫与陷阱

题目&#xff1a; 思路&#xff1a; 代码&#xff1a; #include <bits/stdc.h> using namespace std; const int N1e310; char g[N][N];//输入&#xff1a;图的数组 int vis[N][N]; /* 剪枝&#xff1a;记录magic的个数&#xff08;一个点经过两次&#xff0c;magic越大…

【讲解下如何从零基础学习Java】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

设计模式学习笔记 - 设计模式与范式 -行为型:9.迭代器模式(上):相比直接遍历集合数据,使用迭代器模式有哪些优势?

概述 上篇文章&#xff0c;我们学习了状态模式。状态模式是状态机的一种实现方式。它通过将事件触发的状态转移和动作执行&#xff0c;拆分到不同的状态类中&#xff0c;以此来避免状态机类中的分支判断逻辑&#xff0c;应对状态机类代码的复杂性。 本章&#xff0c;学习另外…

Dude, where’s that IP? Circumventing measurement-based IP geolocation(2010年)

下载地址:https://www.usenix.org/legacy/event/sec10/tech/full_papers/Gill.pdf 被引次数:102 Gill P, Ganjali Y, Wong B. Dude, Wheres That {IP}? Circumventing Measurement-based {IP} Geolocation[C]//19th USENIX Security Symposium (USENIX Security 10). 2010.…