自己做网站翻译服务器 - 添加网站/网上推广app

自己做网站翻译服务器 - 添加网站,网上推广app,开源网站建设教程,seo网站架构设计二叉树与递归 前言226.翻转二叉树算法思路及代码solution 1 用分解问题的思路来解决solution 2 用遍历的思路来解决 101.对称二叉树算法思路及代码solution 104.二叉树的最大深度算法思路及代码solution 1 遍历solution 2 分解问题 111.二叉树的最小深度算法思路及代码solution…

二叉树与递归

  • 前言
    • 226.翻转二叉树
      • 算法思路及代码
        • solution 1 用分解问题的思路来解决
        • solution 2 用遍历的思路来解决
    • 101.对称二叉树
      • 算法思路及代码
        • solution
    • 104.二叉树的最大深度
      • 算法思路及代码
        • solution 1 遍历
        • solution 2 分解问题
    • 111.二叉树的最小深度
      • 算法思路及代码
        • solution 1
        • solution 2
    • 222.完全二叉树的结点个数
      • 算法思路和代码
        • solution 1
        • solution 2
    • 110 平衡二叉树
      • 算法思路及代码
        • solution
    • 257 二叉树的所有路径
      • 算法思路及代码
    • 404 左叶子之和
      • 算法思路与代码
        • solution
    • 513 找树左下角的值
      • 算法思路及代码
        • solution1 (遍历)
        • solution 2(迭代)
    • 112.路径总和
      • 算法思路以及代码
        • solution 1
          • 暴力
        • solution 2
    • 106 中序与后序遍历序列构造二叉树
      • 思路及代码
        • solution
        • 相关题目
    • 654 最大二叉树
      • 思路及代码
        • solution
  • 二叉搜索树相关
    • 700.二叉搜索树中的搜索
      • 算法思路与代码
        • solution

前言

本文是基于代码随想录上二叉树章节的所有例题及其对应的算法思路(序号代表的是力扣题号)

为了避免很多读者看不到最后 我写在最前面 文章总字数预计超过25000字 制作不易 这些都是我在看了很多视频之后整理而成的 希望先收藏点赞起来 以免后续丢失

算法思路主要参考(B站):灵神 代码随想录 labuladong 懒猫老师 想象力少年 在此跪谢!!!
所有题目讲解视频都可以在这里找到
视频合集

不知道从哪入手的 参考我的学习顺序

  • 1 懒猫老师的二叉树章节视频
  • 2 labuladong二叉树纲领篇
  • 3 刷题配合着灵神和官方讲解视频

labuladong二叉树纲领篇——遇到一道二叉树的题目时的通用思考过程是:
1、是否可以通过遍历一遍二叉树得到答案?如果可以,用一个 traverse 函数配合外部变量来实现。
2、是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案?如果可以,写出这个递归函数的定义,并充分利用这个函数的返回值。
3、无论使用哪一种思维模式,你都要明白二叉树的每一个节点需要做什么,需要在什么时候(前中后序)做。

226.翻转二叉树

在这里插入图片描述

算法思路及代码

solution 1 用分解问题的思路来解决

是我在没看题解之前自己想出来的方法
我是怎么想的呢:
我们既然需要翻转整颗二叉树那么我们不妨先翻转左子树,在翻转右子树,然后将跟节点的左右子树交换即可,这样就好啦
递归的结束条件就是节点为空时结束

solution 2 用遍历的思路来解决

这题我们只需要遍历一遍二叉树就可以解决问题,为什么这么说呢?
单独抽出来一个结点来 我们只需要交换左右结点 对不对? 而且我们需要在往下去递归之前做这件事情,也就是在前序位置去交换左右节点 然后其余的结点交给递归就好

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution 1 {
public:TreeNode* invertTree(TreeNode* root) {if(root==nullptr){return nullptr;}TreeNode* right= invertTree(root->left);TreeNode* left= invertTree(root->right);root->right=right;root->left=left;return root;}
};
class Solution 2 {
public:// 主函数TreeNode* invertTree(TreeNode* root) {// 遍历二叉树,交换每个节点的子节点traverse(root);return root;}// 二叉树遍历函数void traverse(TreeNode* root) {if (root == nullptr) {return;}// *** 前序位置 ***// 每一个节点需要做的事就是交换它的左右子节点TreeNode* tmp = root->left;root->left = root->right;root->right = tmp;// 遍历框架,去遍历左右子树的节点traverse(root->left);traverse(root->right);}
};

101.对称二叉树

在这里插入图片描述

算法思路及代码

对于这题 我们先不管代码怎么实现,我们是不是同样遍历一遍二叉树就可以把这个问题解决掉 我们只需要每往下遍历以后 比较它的左右子树即可(后序位置上)
在这里插入图片描述

solution
class Solution {
public:bool compare(TreeNode* left, TreeNode* right) {// 首先排除空节点的情况if (left == NULL && right != NULL) return false;else if (left != NULL && right == NULL) return false;else if (left == NULL && right == NULL) return true;// 排除了空节点,再排除数值不相同的情况else if (left->val != right->val) return false;// 此时就是:左右节点都不为空,且数值相同的情况// 此时才做递归,做下一层的判断bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)return isSame;}bool isSymmetric(TreeNode* root) {if (root == NULL) return true;return compare(root->left, root->right);}
};

104.二叉树的最大深度

在这里插入图片描述

算法思路及代码

solution 1 遍历

对于这道题目我们首先是不是同样可以用遍历的思路来解决,终止条件就是到达叶子节点,我们只需要一个traverse函数和一个外部变量来实现就行,在刚刚进入一个结点的时候(前序位置)去更新我们的maxdepth,在离开一个节点之前(后序位置)去维护depth。

class Solution {public:int depth = 0;int res = 0;int maxDepth(TreeNode* root) {traverse(root);return res;}// 遍历二叉树void traverse(TreeNode* root) {if (root == nullptr) {return;}// 前序遍历位置depth++;// 遍历的过程中记录最大深度res = max(res, depth);traverse(root->left);traverse(root->right);// 后序遍历位置depth--;}
};
solution 2 分解问题

我们要求一颗二叉树的最大深度,我们只需要分别知道它的左右子树的最大深度即可,然后return 1(根节点)+max(leftmax,rightmax)就行了

class Solution2 {// 定义:输入一个节点,返回以该节点为根的二叉树的最大深度
public:int maxDepth(TreeNode* root) {if (root == nullptr) {return 0;}int leftMax = maxDepth(root->left);int rightMax = maxDepth(root->right);// 根据左右子树的最大深度推出原二叉树的最大深度return 1 + max(leftMax, rightMax);}
};

111.二叉树的最小深度

在这里插入图片描述

算法思路及代码

solution 1
// 「遍历」的递归思路
class Solution {
private:int minDepthValue = INT_MAX;int currentDepth = 0;void traverse(TreeNode* root) {if (root == nullptr) {return;}// 做选择:在进入节点时增加当前深度currentDepth++;// 如果当前节点是叶子节点,更新最小深度if (root->left == nullptr && root->right == nullptr) {minDepthValue = std::min(minDepthValue, currentDepth);}traverse(root->left);traverse(root->right);// 撤销选择:在离开节点时减少当前深度currentDepth--;}public:int minDepth(TreeNode* root) {if (root == nullptr) {return 0;}traverse(root);return minDepthValue;}
};
solution 2

我觉得有了上一题的铺垫首先应该是想到这种办法,思路也非常明确,就是想找到左右子树各自的最小深度然后再加上根节点 但是力扣给的示例没有全部通过,它给的示例是一颗斜树,准确的来说是条五个节点的链表,这种就是极端的情况,我们怎么去解决它呢?
根本问题就是要防止当左子树为空时直接return 0的情况发生(参照下面的预期结果),所以我们就需要在即将离开一个节点之前判断此时的左右子树是否为空(也就是leftmin/rightmin为0的情况)
例如 leftmin=0,那么我们就return 1+rightmin(参考根节点的情况)就行了
那为什么在求最大深度的时候不会出现这个问题呢?
给我的感觉: 是因为在求最大深度时,我们的出口是到叶子节点就行(一股脑的往下冲),不会面临着是否为空的问题,
而我们要求最小深度时其实更像是一个探险的人,需要一步一步地走,需要避开危险

deepseek:
最小深度中,当左子树为空时(root.left为None),右子树的深度决定了当前节点的最小深度。同理处理右子树为空的情况。只有左右子树均存在时,才取较小值加1,确保路径有效。(关键)
最大深度直接取左右子树的最大值加1,无需特殊处理空子树,因为空子树的深度0不会影响非空子树的最大值结果

在这里插入图片描述

  • 修改之后AC
class Solution {
public:int minDepth(TreeNode* root) {if(root==nullptr){return 0;}int rightmin=minDepth(root->right);int leftmin=minDepth(root->left);if(leftmin==0){return rightmin+1;}if(rightmin==0){return leftmin+1;}return 1+min(leftmin,rightmin);}
};

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

在这里插入图片描述

算法思路和代码

solution 1

读完这道题我就有思路了,就是把左右子树的分别有多少个节点给他算出来,然后相加不就行了吗 确实能AC,也很简单
但是这题特意说了是完全二叉树的节点 意味着我们其实是可以利用完全二叉树的特性来解决问题的,这才是这题高效率的关键,因此就有了solution2

class Solution {
public:int countNodes(TreeNode* root) {if(root==nullptr){return 0;}int left=countNodes(root->left);int right=countNodes(root->right);int result=left+right+1;return result;}
};
solution 2

在这里插入图片描述
假如你学习过懒猫老师的课程,你一定知道如果给你一个满二叉树的深度k,那么它的节点数就是(2的k次方)-1,而给你一个二叉树的层数x,那么这一层有2的(X-1)次幂 个节点

#include <cmath>class Solution {
public:int countNodes(TreeNode* root) {TreeNode* l = root;TreeNode* r = root;// 记录左、右子树的高度int hl = 0, hr = 0;while (l != nullptr) {l = l->left;hl++;}while (r != nullptr) {r = r->right;hr++;}// 如果左右子树的高度相同,则是一棵满二叉树if (hl == hr) {return (int)pow(2, hl) - 1;}// 如果左右高度不同,则按照普通二叉树的逻辑计算return 1 + countNodes(root->left) + countNodes(root->right);}
};

110 平衡二叉树

在这里插入图片描述
一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1

算法思路及代码

solution

对于这道题其实不用想的太复杂,还是一样,就问你是不是遍历一遍就可以判断是否为二叉平衡树了,怎么判断呢,是不是把两个左右子树高度搞出来,然后比较一下就行了,代码上就是借助一个外部变量然后一个compare函数就行 需要注意的是定义里提到了绝对值,那么最简单能准确表达的 就是借助abs绝对值函数,会用就行

  • 我们需要知道这个compare函数帮我们做了什么事情,需要明确给出这个函数的定义:返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1 不然就很容易被绕进去

  • 明确单层递归的逻辑
    如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。

  • 分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。

class Solution {
public:int compare_height(TreeNode* root){if(root==nullptr){return 0;}int left=compare_height(root->left);if(left==-1){return -1;}int right=compare_height(root->right);if(right==-1){return -1;}return abs(left - right) > 1 ? -1 : 1 + max(left, right);}bool isBalanced(TreeNode* root) {int result= compare_height(root);if(result!=-1){return true;}else{return false;}}
};

257 二叉树的所有路径

在这里插入图片描述
这题其实还是有一些不一样 回溯的味道非常明显了 这也是labuladong在它的文章中提到的二叉树与动态规划和回溯算法之间的关系

算法思路及代码

在这里插入图片描述

404 左叶子之和

在这里插入图片描述

算法思路与代码

在这里插入图片描述

solution
class Solution {
public:int sumOfLeftLeaves(TreeNode* root) {if(root==nullptr){return 0;}if(root->left==nullptr &&root->right==nullptr)//叶子节点{return 0;}int leftsum=sumOfLeftLeaves(root->left);if(root->left!=nullptr && root->left->left==nullptr && root->left->right==nullptr){leftsum=root->left->val;}int rightsum=sumOfLeftLeaves(root->right);int sum=leftsum+rightsum;return sum;}
};

513 找树左下角的值

在这里插入图片描述

算法思路及代码

solution1 (遍历)

对于这题来说 (详细的过程都在代码随想录)详细版

  • 首先 思路是先用遍历的方式来解决 一个find函数和一个变量depth就能解决问题
  • 其次我们需要确定这个函数的终止条件(递归的出口)是不是当遇到叶子节点的时候就需要更新depth了,用一个result记录此时深度最左边节点的值
  • 再次我们需要给这个find函数一个明确的定义,就是给一个根节点返回,最大深度最左边孩子的值
  • 细节方面 由于求得是最大深度 我们的depth是需要不断更新的 就是说我们需要一个全局变量保存depth的值 由于我们需要再往下遍历之前记录此时的深度 所以还是采用前序遍历
  • 先给MAXdepth先定义成最小的整型,确保MAXdepth能够正常更新 result同理
class Solution {
public:
int Maxdepth=INT_MIN;
int result;void traverse(TreeNode* root,int depth){if(root->left==nullptr&& root->right==nullptr){//如果找到叶子节点更新最大深度if(depth>Maxdepth){Maxdepth=depth;result=root->val;}return;}if(root->left!=nullptr){depth++;//前序位置traverse(root->left,depth);depth--;//后序位置 回溯(在离开节点时维护depth)}if(root->right!=nullptr){depth++;traverse(root->right,depth);depth--;}return;}int findBottomLeftValue(TreeNode* root) {traverse(root,0);return result;}
solution 2(迭代)

我们只需要在层序遍历到最后一层时 记录最后一层的第一个节点即可
利用了C++STL容器和队列先进先出的特点 定义了一个结点类型的队列 用于存储树的节点 弄个node指向每一层的第一个节点,result记录每次node指向的值,直到队空停止循环

class Solution {
public:int findBottomLeftValue(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);int result = 0;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;}
};

112.路径总和

在这里插入图片描述

算法思路以及代码

思路概述
我们需要判断二叉树中是否存在从根节点到叶子节点的路径,使得路径上所有节点值的和等于给定的目标和 targetSum。我们可以通过递归的方式实现这一点,具体来说,使用前序遍历(根-左-右)来遍历二叉树。
详细步骤
定义 findPath 函数:
参数:
cur:当前处理的节点。
sum:当前路径的累加和。
targetSum:目标和。
返回值:布尔值,表示是否存在从当前节点到叶子节点的路径,路径和等于 targetSum。
检查空节点:
如果当前节点 cur 为空,直接返回 false,因为空节点不可能构成路径。
累加节点值:

将当前节点的值 cur->val 累加到路径和 sum 中。
检查叶子节点:
如果当前节点是叶子节点(即没有左子节点和右子节点),检查路径和 sum 是否等于目标和 targetSum。
如果相等,返回 true,表示找到了满足条件的路径。
否则,返回 false。
递归检查左子树:
递归调用 findPath 处理左子树 cur->left。
如果左子树中存在满足条件的路径,返回 true。
递归检查右子树:
递归调用 findPath 处理右子树 cur->right。
如果右子树中存在满足条件的路径,返回 true。
返回结果:
如果左右子树中都没有找到满足条件的路径,返回 false。

solution 1
暴力

一开始我想用一个vector向量来保存每次递归到叶子节点sum的值 也就是说需要遍历出所有路线,然后在主函数中for循环遍历我们的vector 如果找到了targetsum就return true 找不到就return false

#include <iostream>
#include <vector>using namespace std;// Definition for a binary tree node.
struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode() : val(0), left(nullptr), right(nullptr) {}TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};class Solution {
public:// 辅助函数,用于递归查找路径和并存储在 record 向量中void findPath(TreeNode* cur, int sum, int targetSum, vector<int>& record) {// 如果当前节点为空,直接返回if (cur == nullptr) {return;}// 累加当前节点的值到路径和sum += cur->val;// 检查当前节点是否是叶子节点(即没有左子节点和右子节点)if (cur->left == nullptr && cur->right == nullptr) {// 如果是叶子节点,将路径和添加到 record 向量中record.push_back(sum);return;}// 递归检查左子树findPath(cur->left, sum, targetSum, record);// 递归检查右子树findPath(cur->right, sum, targetSum, record);}// 主函数,判断是否存在路径和等于目标和bool hasPathSum(TreeNode* root, int targetSum) {vector<int> record; // 用于存储每条路径的和findPath(root, 0, targetSum, record); // 调用辅助函数 findPath// 遍历 record 向量,检查是否存在等于 targetSum 的路径和for (int sum : record) {if (sum == targetSum) {return true;}}// 如果没有找到等于 targetSum 的路径和,返回 falsereturn false;}
};

在这里插入图片描述

但是这个思路确实是太麻烦了是可以去优化的
在这里插入图片描述

class Solution {
public:// 辅助函数,用于递归查找路径和bool findPath(TreeNode* cur, int sum, int targetSum) {// 如果当前节点为空,直接返回 falseif (cur == nullptr) {return false;}// 累加当前节点的值到路径和sum += cur->val;// 检查当前节点是否是叶子节点(即没有左子节点和右子节点)if (cur->left == nullptr && cur->right == nullptr) {// 如果路径和等于目标和,返回 truereturn sum == targetSum;}// 递归检查左子树,如果左子树中存在满足条件的路径,返回 trueif (findPath(cur->left, sum, targetSum)) {return true;}// 递归检查右子树,如果右子树中存在满足条件的路径,返回 trueif (findPath(cur->right, sum, targetSum)) {return true;}// 如果左右子树中都没有找到满足条件的路径,返回 falsereturn false;}// 主函数,判断是否存在路径和等于目标和bool hasPathSum(TreeNode* root, int targetSum) {// 调用辅助函数 findPath 从根节点开始查找return findPath(root, 0, targetSum);}
};

在这里插入图片描述

solution 2

其实跟方法一在思路上本质上是一样的

class Solution 2 {
public:// 辅助函数,用于递归查找路径和bool findPath(TreeNode* cur, int sum, int targetSum) {// 如果当前节点为空,直接返回 falseif (cur == nullptr) {return false;}// 累加当前节点的值到路径和sum += cur->val;// 检查当前节点是否是叶子节点(即没有左子节点和右子节点)if (cur->left == nullptr && cur->right == nullptr) {// 如果路径和等于目标和,返回 truereturn sum == targetSum;}// 递归检查左子树,如果左子树中存在满足条件的路径,返回 trueif (findPath(cur->left, sum, targetSum)) {return true;}// 递归检查右子树,如果右子树中存在满足条件的路径,返回 trueif (findPath(cur->right, sum, targetSum)) {return true;}// 如果左右子树中都没有找到满足条件的路径,返回 falsereturn false;}// 主函数,判断是否存在路径和等于目标和bool hasPathSum(TreeNode* root, int targetSum) {// 调用辅助函数 findPath 从根节点开始查找return findPath(root, 0, targetSum);}
};

106 中序与后序遍历序列构造二叉树

在这里插入图片描述

我觉得一上来就做这题以及后面的同类型题目其实还是比较困难的 因为我们一开始在学习二叉树的前中后序遍历时一般都是给我们一颗二叉树 让我们给出它的前中后序的遍历序列 这是一个正向的过程 而本题刚刚好是不是完全相反了啊 所以比较难
希望大家在做这题之前去看一下懒猫老师的视频 如果不明白C++ STL的map以及它的基本操作 也需要去了解一下

懒猫老师-数据结构-(29)根据二叉树的遍历结果确定二叉树
如果你看完这个视频 相信你一定对于这题有了最基本的认知,只不过对于代码实现还是不太清楚
也就是说 你知道给你一个前序和中序/后序遍历序列,能确定唯一的二叉树 你在看后面的题目 给你一个前序后序 题目只要你返回任意一颗满足的二叉树即可 这就是原因所在
其实我觉得区别这些的关键其实就是对于每颗子树的根节点怎么去找

思路及代码

主函数是buildTree 辅助函数是build 哈希表inpos的作用就是存储中序遍历的值与其位置的映射比如说:后序遍历的最后一个元素是根节点 我们现在想确定它的左右子树 那么我们只需要找到这个根节点在中序遍历上的位置即可 左边就是左子树 右边就是右子树

solution

il ,ir分别表示前序遍历序列的左右两端 pl,pr分别表示后序遍历的左右两端(我用AI补全了注释方便理解)
如果还有不懂的地方直接看视频就好 我附在这里 真的是讲的非常好
106. 从中序与后序遍历序列构造二叉树 | 手写图解版思路 + 代码讲解


// Solution类用于根据中序和后序遍历数组构建二叉树
class Solution {// 使用哈希表存储中序遍历中每个值的位置,以便快速查找根节点在中序遍历中的位置unordered_map<int,int>inpos;
public:/*** 主要的构建函数,接收中序和后序遍历数组作为输入* @param inorder 中序遍历数组* @param postorder 后序遍历数组* @return 返回构建的二叉树根节点*/TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {int n=inorder.size();// 遍历中序遍历数组,记录每个值的位置for(int i=0;i<n;i++){inpos[inorder[i]]=i;}// 调用build函数开始构建二叉树return build(inorder,postorder,0,n-1,0,n-1);}/*** 递归构建二叉树的辅助函数* @param inorder 中序遍历数组* @param postorder 后序遍历数组* @param il 中序遍历的左边界* @param ir 中序遍历的右边界* @param pl 后序遍历的左边界* @param pr 后序遍历的右边界* @return 返回当前递归构建的子树根节点*/TreeNode* build(vector<int>& inorder,vector<int>& postorder,int il,int ir,int pl,int pr){// 如果中序遍历的左边界大于右边界,说明已经没有节点,返回空指针if(il>ir || pl>pr){return nullptr;}// 计算左子树的节点个数int k=inpos[postorder[pr]]-il;// 获取当前子树的根节点值int rootval=postorder[pr];// 创建根节点TreeNode* root =new TreeNode(rootval);// 递归构建左子树root->left=build(inorder,postorder,il,il+k-1,pl,pl+k-1);// 递归构建右子树root->right=build(inorder,postorder,il+k+1,ir,pl+k,pr-1);// 返回构建的根节点return root;}
};
相关题目

前序中序

TreeNode* constructMaximumBinaryTree([3,2,1,6,0,5]) {// 找到数组中的最大值TreeNode* root = new TreeNode(6);// 递归调用构造左右子树root->left = constructMaximumBinaryTree({3,2,1});root->right = constructMaximumBinaryTree({0,5});return root;
}// 当前 nums 中的最大值就是根节点,然后根据索引递归调用左右数组构造左右子树即可
// 再详细一点,就是如下伪码
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {if (nums.empty()) return nullptr;// 找到数组中的最大值int maxVal = INT_MIN;int index = 0;for (int i = 0; i < nums.size(); i++) {if (nums[i] > maxVal) {maxVal = nums[i];index = i;}}TreeNode* root = new TreeNode(maxVal);// 递归调用构造左右子树root->left = constructMaximumBinaryTree(nums[0..index-1]);root->right = constructMaximumBinaryTree(nums[index+1..nums.size()-1]);
}

前序后序(不唯一)

class Solution {// 存储 postorder 中值到索引的映射unordered_map<int, int> valToIndex;public:TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {for (int i = 0; i < postorder.size(); i++) {valToIndex[postorder[i]] = i;}return build(preorder, 0, preorder.size() - 1,postorder, 0, postorder.size() - 1);}// 定义:根据 preorder[preStart..preEnd] 和 postorder[postStart..postEnd]// 构建二叉树,并返回根节点。TreeNode* build(vector<int>& preorder, int preStart, int preEnd,vector<int>& postorder, int postStart, int postEnd) {if (preStart > preEnd) {return nullptr;}if (preStart == preEnd) {return new TreeNode(preorder[preStart]);}// root 节点对应的值就是前序遍历数组的第一个元素int rootVal = preorder[preStart];// root.left 的值是前序遍历第二个元素// 通过前序和后序遍历构造二叉树的关键在于通过左子树的根节点// 确定 preorder 和 postorder 中左右子树的元素区间int leftRootVal = preorder[preStart + 1];// leftRootVal 在后序遍历数组中的索引int index = valToIndex[leftRootVal];// 左子树的元素个数int leftSize = index - postStart + 1;// 先构造出当前根节点TreeNode* root = new TreeNode(rootVal);// 递归构造左右子树// 根据左子树的根节点索引和元素个数推导左右子树的索引边界root->left = build(preorder, preStart + 1, preStart + leftSize,postorder, postStart, index);root->right = build(preorder, preStart + leftSize + 1, preEnd,postorder, index + 1, postEnd - 1);return root;}
};

654 最大二叉树

在这里插入图片描述

思路及代码

*每个二叉树节点都可以认为是一棵子树的根节点,对于根节点,首先要做的当然是把想办法把自己先构造出来,然后想办法构造自己的左右子树。

所以,我们要遍历数组把找到最大值 maxVal,从而把根节点 root 做出来,然后对 maxVal 左边的数组和右边的数组进行递归构建,作为 root 的左右子树。*

solution
class Solution1 {
public:TreeNode* constructMaximumBinaryTree(vector<int>& nums) {//递归终止条件:只有一个元素的时候if(nums.size()==1)return new TreeNode (nums[0]);//我们需要记录最大值来输出 以及索引下标来分隔左子树 右子树int maxval=0;int index=0;//遍历整个数组找到最大值for(int i=0;i<nums.size();i++){if(nums[i]>maxval){maxval=nums[i];index=i;//不断更新maxval//直到遍历完整个数组找到最大值}}TreeNode* root=new TreeNode(maxval);//递归创建左子树if(index>0){vector<int>vec(nums.begin(),nums.begin()+index) ;//创建一个数组来存储左子树元素范围root->left=constructMaximumBinaryTree(vec);}//右子树if(index<nums.size()-1)//说明至少还有一个元素{vector<int>vec2(nums.begin()+index+1,nums.end());root->right=constructMaximumBinaryTree(vec2);}return root;}
};
**************************************
class Solution2 {
public:// 主函数TreeNode* constructMaximumBinaryTree(std::vector<int>& nums) {return build(nums, 0, nums.size() - 1);}// 定义:将 nums[lo..hi] 构造成符合条件的树,返回根节点TreeNode* build(std::vector<int>& nums, int lo, int hi) {// base caseif (lo > hi) {return nullptr;}// 找到数组中的最大值和对应的索引int index = -1, maxVal = INT_MIN;for (int i = lo; i <= hi; i++) {if (maxVal < nums[i]) {index = i;maxVal = nums[i];}}TreeNode* root = new TreeNode(maxVal);// 递归调用构造左右子树root->left = build(nums, lo, index - 1);root->right = build(nums, index + 1, hi);return root;}
};

二叉搜索树相关

700.二叉搜索树中的搜索

在这里插入图片描述

算法思路与代码

就是利用二叉搜索树左<根<右的特性 使得我们不用去搜索整颗二叉树去找到target

solution
class Solution {
public:TreeNode* searchBST(TreeNode* root, int val) {if(root==nullptr || root->val==val){return root;}       if(root->val>val){return searchBST(root->left,val);}if(root->val<val){return searchBST(root->right,val);}return nullptr;}
};

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

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

相关文章

MyBatis映射文件 <resultMap> 元素详解与示例

引言 <resultMap> 是 MyBatis 中最核心的映射配置元素&#xff0c;用于解决数据库字段与 Java 对象属性之间的复杂映射问题&#xff0c;尤其是字段名不一致、嵌套对象关联、集合映射等场景。ResultMap 的设计思想是&#xff0c;对简单的语句做到零配置&#xff0c;对于复…

WIN11上使用GraalVM打包springboot3项目为本地可执行文件exe

耐心肝才能成功 概念步骤概要详细步骤一. GraalVM 17二. 安装Visual Studio 2022三. 创建springboot四. IDEA最新版或者eclipse2025调试项目五. 打包exe 概念 springboot3生成的jar编译成windows本地C文件&#xff0c;不再依赖JVM运行 WINDOW编译较为复杂&#xff0c;限制条件…

【git-hub项目:YOLOs-CPP】本地实现01:项目构建

目录 写在前面 项目介绍 最新发布说明 Segmentation示例 功能特点 依赖项 安装 克隆代码仓库 配置 构建项目 写在前面 前面刚刚实现的系列文章: 【Windows/C++/yolo开发部署01】 【Windows/C++/yolo开发部署02】 【Windows/C++/yolo开发部署03】 【Windows/C++/yolo…

超越 DeepSeek V3 -->【Qwen2.5-Max】

&#x1f525; 先说明&#xff0c;不是广子&#xff0c;不是广子&#xff01;&#xff01;&#xff01;单纯分享这个工具给大家&#xff0c;毕竟最近使用 DeepSeek 太容易崩了&#xff0c;每天深度思考一次之后就开始转圈圈用不了&#xff0c;然后就找到了这个工具使用 一、前言…

python自动化测试之Pytest框架之YAML详解以及Parametrize数据驱动!

一、YAML详解 YAML是一种数据类型&#xff0c;它能够和JSON数据相互转化&#xff0c;它本身也是有很多数据类型可以满足我们接口 的参数类型&#xff0c;扩展名可以是.yml或.yaml 作用&#xff1a; 1.全局配置文件 基础路径&#xff0c;数据库信息&#xff0c;账号信息&…

CentOS 7操作系统部署KVM软件和创建虚拟机

CentOS 7.9操作系统部署KVM软件和配置指南&#xff0c;包括如何创建一个虚拟机。 步骤 1: 检查硬件支持 首先&#xff0c;确认您的CPU支持虚拟化技术&#xff0c;并且已在BIOS中启用&#xff1a; egrep -c (vmx|svm) /proc/cpuinfo 如果输出大于0&#xff0c;则表示支持虚拟…

日本 万叶假名

万叶假名&#xff08;まんようがな&#xff0c;Manyōgana&#xff09;是一种早期的日语书写系统&#xff0c;主要用于《万叶集》等古代文献中。它的特点是完全使用汉字来表示日语的音&#xff0c;不考虑汉字的原意。可以将其视为平假名和片假名的前身。 记住是唐代的发音不是…

【鸿蒙HarmonyOS Next实战开发】实现组件动态创建和卸载-优化性能

一、简介 为了解决页面和组件加载缓慢的问题&#xff0c;ArkUI框架引入了动态操作功能&#xff0c;支持组件的预创建&#xff0c;并允许应用在运行时根据实际需求动态加载和渲染组件。 这些动态操作包括动态创建组件&#xff08;即动态添加组件&#xff09;和动态卸载组件&am…

MongoDB 7 分片副本集升级方案详解(上)

#作者&#xff1a;任少近 文章目录 前言&#xff1a;Mongodb版本升级升级步骤环境1.1环境准备1.2standalone升级1.3分片、副本集升级 前言&#xff1a;Mongodb版本升级 在开始升级之前&#xff0c;请参阅 MongoDB下个版本中的兼容性变更文档&#xff0c;以确保您的应用程序和…

AI前端开发:跨领域合作的新引擎

随着人工智能技术的飞速发展&#xff0c;AI代码生成器等工具的出现正深刻地改变着软件开发的模式。 AI前端开发的兴起&#xff0c;不仅提高了开发效率&#xff0c;更重要的是促进了跨领域合作&#xff0c;让数据科学家、UI/UX设计师和前端工程师能够更紧密地协同工作&#xff0…

DeepSeek 助力 Vue 开发:打造丝滑的返回顶部按钮(Back to Top)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

Java练习(20)

ps:练习来自力扣 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 class Solution {pu…

Tetragon:一款基于eBPF的运行时环境安全监控工具

关于Tetragon Tetragon是一款基于eBPF的运行时环境安全监控工具&#xff0c;该工具可以帮助广大研究人员检测并应对安全重大事件&#xff0c;例如流程执行事件、系统调用活动、I/O活动&#xff08;包括网络和文件访问等&#xff09;。 在 Kubernetes 环境中使用时&#xff0c;…

Qt多线程技术【线程池】:QRunnable 和 QThreadPool

在现代软件开发中&#xff0c;尤其是在处理大量并发任务时&#xff0c;线程池技术是一种高效的解决方案。线程池不仅能提高程序的性能&#xff0c;还能有效管理线程的生命周期&#xff0c;避免频繁的线程创建和销毁所带来的性能损失。本文将以Qt中的 QThreadPool 和 QRunnable …

218.子结构判断

class Solution {/*** 判断树 B 是否是树 A 的子结构* param A 树 A 的根节点* param B 树 B 的根节点* return 如果 B 是 A 的子结构&#xff0c;返回 true&#xff1b;否则返回 false*/public boolean isSubStructure(TreeNode A, TreeNode B) {// 如果树 B 为空&#xff0c;…

Navicat导入海量Excel数据到数据库(简易介绍)

目录 前言正文 前言 此处主要作为科普帖进行记录 原先Java处理海量数据的导入时&#xff0c;由于接口超时&#xff0c;数据处理不过来&#xff0c;后续转为Navicat Navicat 是一款功能强大的数据库管理工具&#xff0c;支持多种数据库系统&#xff08;如 MySQL、PostgreSQL、…

文化财经t8优质短线期货交易量化模型源码

// 参数设置 BOLL_PERIOD : 20; // 布林带周期 RSI_PERIOD : 14; // RSI 周期 OVERSOLD : 30; // 超卖线 OVERBOUGHT : 70; // 超买线 // 计算布林带 MID : MA(CLOSE, BOLL_PERIOD); UPPER : MID 2 * STD(CLOSE, BOLL_PERIOD); LOWER : MID - 2 * STD(CLOSE,…

[AI]Mac本地部署Deepseek R1模型 — — 保姆级教程

[AI]Mac本地部署DeepSeek R1模型 — — 保姆级教程 DeepSeek R1是中国AI初创公司深度求索&#xff08;DeepSeek&#xff09;推出大模型DeepSeek-R1。 作为一款开源模型&#xff0c;R1在数学、代码、自然语言推理等任务上的性能能够比肩OpenAI o1模型正式版&#xff0c;并采用MI…

【UE5】PeerStream像素流部署

视频教程 https://www.bilibili.com/video/BV1GhiuecEpK?spm_id_from333.788.videopod.sections&vd_source02dd8acc3a83a728e375ff61f1ebe725步骤 下载PeerStream代码 代码结构和项目如图 github地址:https://github.com/inveta/PeerStreamEnterprise下载node node 对应…

老牌系统工具箱,现在还能打!

今天给大家分享一款超实用的电脑软硬件检测工具&#xff0c;虽然它是一款比较“资深”的软件&#xff0c;但依然非常好用&#xff0c;完全能满足我们的日常需求。 电脑软硬件维护检测工具 功能强大易用 这款软件非常贴心&#xff0c;完全不需要安装&#xff0c;直接打开就能用…