leetcode 105. 从前序与中序遍历序列构造二叉树(哈希)

在这里插入图片描述

		/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    unordered_map<int,int>ans;
    TreeNode* dfs(vector<int>& pre, vector<int>& in,int pl,int pr,int il,int ir)//pl是指当前pl根节点在前序的范围,后面是在中序                                                                                                                  //的范围
    {
      if(pl>pr)return nullptr;//不存在范围说明为空
      int rec=pre[pl];//当前根的元素值
      int k=ans[rec];//根在中序遍历的下标
      int length=k-il;//根左孩子范围
      auto root=new TreeNode(rec);
      root->left=dfs(pre,in,pl+1,pl+length,il,k-1);//pl+length为pl左树的上界,k-1为中序的上界;
      root->right=dfs(pre,in,pl+1+length,pr,k+1,ir);
      return root;
      
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
     int n=inorder.size();
     for(int i=0;i<n;i++)ans[inorder[i]]=i;//把中序下标存入哈希表
     return dfs(preorder,inorder,0,n-1,0,n-1);
    }
};
考虑柔性负荷的综合能源系统低碳经济优化调度【考虑碳交易机制】(Matlab代码实现)内容概要:本文围绕“考虑柔性负荷的综合能源系统低碳经济优化调度”展开,重点研究在碳交易机制下如何实现综合能源系统的低碳化经济性协同优化。通过构建包含风电、光伏、储能、柔性负荷等多种能源形式的系统模型,结合碳交易成本能源调度成本,提出优化调度策略,以降低碳排放并提升系统运行经济性。文中采用Matlab进行仿真代码实现,验证了所提模型在平衡能源供需、平抑可再生能源波动、引导柔性负荷参调度等方面的有效性,为低碳能源系统的设计运行提供了技术支撑。; 适合人群:具备一定电力系统、能源系统背景,熟悉Matlab编程,从事能源优化、低碳调度、综合能源系统等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①研究碳交易机制对综合能源系统调度决策的影响;②实现柔性负荷在削峰填谷、促进可再生能源消纳中的作用;③掌握基于Matlab的能源系统建模优化求解方法;④为实际综合能源项目提供低碳经济调度方案参考。; 阅读建议:建议读者结合Matlab代码深入理解模型构建求解过程,重点关注目标函数设计、约束条件设置及碳交易成本的量化方式,可进一步扩展至多能互补、需求响应等场景进行二次开发仿真验证。
<think>好的,我现在需要解决用户的问题:如何根据给定的前序遍历和中序遍历序列构建二叉树,并且使用C++实现。让我仔细理清思路,确保自己正确理解每一步,避免犯错。 首先,用户提供的前序遍历是3、9、20、15、7,中序遍历是9、3、15、20、7。我需要回忆二叉树遍历的基本知识。前序遍历的顺是根节点-左子树-右子树,而中序遍历是左子树-根节点-右子树。因此,前序遍历的第一个元素就是整个树的根节点,这里是3。接下来,我需要在中序遍历中找到这个根节点,根节点左边的部分是左子树的中序遍历,右边是右子树的中序遍历。 接下来,我需要确定左子树和右子树的大小。例如,中序遍历中根节点3左边的元素是9,说明左子树只有一个节点9。右边的部分是15、20、7,这是右子树的中序遍历。对应的前序遍历中,根节点之后的部分应该先是左子树的前序遍历,然后是右子树的前序遍历。由于左子树只有一个节点,所以前序遍历中根3之后的下一个元素9就是左子树的根,剩下的20、15、7是右子树的前序遍历部分。 现在,我需要递归地构建左右子树。对于左子树,前序遍历只剩下9,中序遍历也是9,所以左子树只有一个节点9。右子树的前序遍历是20、15、7,中序遍历是15、20、7。这时候,右子树的根节点是前序遍历的第一个元素20。然后在中序遍历中找到20的位置,左边是15,右边是7。所以右子树的左子树是15,右子树是7。对应的前序遍历中,20后面跟着的15和7应该分为左子树和右子树的前部分,这里可能需要进一步分析。 不过,这里可能存在一个问题:右子树的前序遍历是20、15、7,而中序遍历是15、20、7。所以,根节点是20,左子树的中是15,右子树的中是7。那么前序遍历中,根20之后的15应该是左子树的根,剩下的7是右子树的根。这样,右子树的结构应该是根20,左子节点15,右子节点7。 接下来,我需要考虑如何将这些步骤转化为C++代码。通常,构建二叉树需要递归的方法。递归函数需要接收当前子树的前序遍历和中序遍历的区间,然后根据根节点划分左右子树,并递归构建左右子树。 可能的步骤: 1. 确定根节点的值,即前序遍历的第一个元素。 2. 在中序遍历中找到根节点的位置,分割左右子树的中序遍历序列。 3. 根据左子树的大小,分割前序遍历序列为左子树的前和右子树的前。 4. 递归构建左子树和右子树。 需要注意的是,分割时数组的索引是否正确。例如,如果中序遍历中根的位置是index,那么左子树的大小是index - inStart。前序遍历中左子树的部分是从preStart+1到preStart + leftSize,右子树则是剩下的部分。 现在,我需要考虑代码的具体实现。首先定义二叉树的结构,每个节点有值、左子节点和右子节点。然后编写递归函数,参数包括前序遍历的起始和结束索引,中序遍历的起始和结束索引。使用哈希表存储中序遍历的值到索引的映射,以便快速查找根节点位置。 可能的边界情况包括当中序遍历的起始大于结束索引时,返回nullptr。例如,当左子树不存在时,左子树的递归调用应该终止。 在具体实现中,需要注意数组的索引是否正确。例如,前序遍历的起始位置每次取第一个元素作为根,然后左子树的前序遍历范围是preStart+1到preStart + leftSize,右子树的前序遍历范围是preStart + leftSize +1到preEnd。中序遍历的左子树范围是inStart到rootIndex-1,右子树是rootIndex+1到inEnd。 现在,我需要验证这个思路是否正确。例如,给出的例子: 前:3,9,20,15,7 中:9,3,15,20,7 根是3,在中中的位置是索引1。左子树的中是9(索引0到0),左子树的前应该是前中根之后的一个元素,即9。右子树的中是15,20,7(索引2到4)。对应的右子树的前是20,15,7。此时,右子树的根是20,在中中的位置是索引2。左子树的中是15(索引2到1?这里可能有问题,需要仔细检查)。 哦,这里似乎有错误。中中根是3的位置是索引1,所以右子树的中应该从索引2到4,即15,20,7。那么右子树的根是前中的下一个元素,即20。在中的右子树部分(15,20,7)中,20的位置是索引3。因此,右子树的左子树是15(索引2到2),右子树是7(索引4到4)。 对应的前部分,右子树的前是20,15,7。左子树的大小是1(中中的15个数),所以前中左子树的部分是15,右子树的部分是7。因此,根20的左子节点是15,右子节点是7。这样,整个树的结构是正确的。 现在,我需要确保代码中的索引分割正确。例如,在构建右子树时: 前的起始位置是preStart + leftSize +1?或者需要计算左子树的大小?例如,左子树的大小是rootIndex - inStart。对于整个树的根3,左子树的大小是1(rootIndex=1,inStart=0),所以左子树的前结束位置是preStart + leftSize = 0 +1=1。右子树的前起始位置是preStart + leftSize +1=1+1=2,结束位置是preEnd=4。这样,右子树的前是20,15,7,正确。 同样的,在右子树的递归调用中,根是20,在中中的位置是3(在全局的中数组中的索引3),而右子树的中范围是原来的右子树的范围,即inStart=2,inEnd=4。此时,rootIndex=3(在中中的位置)。左子树的大小为rootIndex - inStart =3-2=1。因此,右子树的左子树的前范围是preStart+1到preStart+1(假设preStart是2,即右子树的前起始是2),所以左子树的前结束是2+1=3,对应的元素是15。右子树的前起始是3+1=4,元素是7。 因此,递归构建是正确的,最终树的结构应该是: 根3,左子节点9,右子节点20。20的左子节点15,右子节点7。这符合用户给出的前和中序遍历的结果。 接下来,我需要将这些步骤用C++代码实现。需要注意的是,使用哈希表来快速查找中的索引。另外,递归函数的参数需要包括前和中的数组,以及当前的索引范围。由于C++中数组可以通过指针或vector传递,这里可能需要用索引来标记范围。 可能的代码结构: 定义树节点结构体: struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; 然后,编写构建函数: class Solution { public: TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { // 创建哈希表存储中序遍历的索引 unordered_map<int, int> index_map; for (int i = 0; i < inorder.size(); ++i) { index_map[inorder[i]] = i; } return buildHelper(preorder, 0, preorder.size() -1, inorder, 0, inorder.size()-1, index_map); } private: TreeNode* buildHelper(vector<int>& preorder, int preStart, int preEnd, vector<int>& inorder, int inStart, int inEnd, unordered_map<int, int>& index_map) { if (preStart > preEnd || inStart > inEnd) { return nullptr; } int rootVal = preorder[preStart]; TreeNode* root = new TreeNode(rootVal); int rootIndex = index_map[rootVal]; // 在中中的位置 int leftSize = rootIndex - inStart; // 左子树的大小 // 构建左子树 root->left = buildHelper(preorder, preStart +1, preStart + leftSize, inorder, inStart, rootIndex -1, index_map); // 构建右子树 root->right = buildHelper(preorder, preStart + leftSize +1, preEnd, inorder, rootIndex +1, inEnd, index_map); return root; } }; 需要测试这段代码是否正确处理给定的例子。例如,前数组是[3,9,20,15,7],中是[9,3,15,20,7]。调用buildTree函数后,应该构建正确的树结构。 可能的测试用例: 当preStart=0,preEnd=4,inStart=0,inEnd=4时: 根是3,rootIndex=1。leftSize=1-0=1。左子树的preStart=1,preEnd=1(preStart+leftSize=0+1=1)。中的inStart=0,inEnd=0。递归构建左子树,此时preStart=1,preEnd=1,中的inStart=0,inEnd=0,根是9,左右子树为空。 右子树的preStart=0+1+1=2,preEnd=4。中的inStart=2,inEnd=4。根是20,rootIndex=3(在中数组中的索引3)。leftSize=3-2=1。左子树的preStart=2+1=3,preEnd=3(preStart + leftSize=2+1=3?或者原preStart是2,preStart+1=3,preEnd=2+1=3)。中的inStart=2,inEnd=3-1=2。左子树的根是15,左右子树为空。右子树的preStart=3+1=4,preEnd=4,中的inStart=4,inEnd=4,根是7,左右子树为空。 因此,最终的树结构正确。 需要注意的是,在传递preStart和preEnd时,是否正确计算。例如,右子树的preStart应该是preStart + leftSize +1,而preEnd保持不变。左子树的preEnd是preStart + leftSize。 现在,检查代码中的参数是否正确: root->left的preStart是preStart+1,preEnd是preStart + leftSize。因为左子树的前部分是从preStart+1开始,长度是leftSize。例如,原preStart是0,leftSize是1,所以preEnd是0+1=1。这是正确的。 root->right的preStart是preStart + leftSize +1,即0+1+1=2,preEnd保持4。这样,右子树的前是2到4,即20,15,7。正确。 同样,在右子树递归时,rootVal是20,rootIndex是3,inStart=2,inEnd=4。leftSize=3-2=1。左子树的preStart是2+1=3,preEnd=2+1=3。对应的前元素是15。中的inStart=2,inEnd=3-1=2。正确。 因此,代码应该能正确构建二叉树。 总结,用户的问题可以通过递归方法解决,关键点在于正确分割前和中数组的索引,并递归构建左右子树。代码实现需要注意索引的计算,避免越界或错误分割。</think>以下是基于前序遍历和中序遍历构建二叉树的 **C++ 实现**,结合代码注释和示例分析: --- ### **1. 核心原理** - **前序遍历**:根节点 → 左子树 → 右子树(示例:`[3,9,20,15,7]`)。 - **中序遍历**:左子树 → 根节点 → 右子树(示例:`[9,3,15,20,7]`)。 - **关键步骤**: 1.序列的第一个元素是当前子树的根节点。 2. 在中序列中找到根节点位置,分割左右子树。 3. 递归构建左子树和右子树。 --- ### **2. C++ 代码实现** ```cpp #include <vector> #include <unordered_map> using namespace std; // 二叉树节点定义 struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; class Solution { public: TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { // 哈希表快速定位中序遍历中的根节点位置 unordered_map<int, int> index_map; for (int i = 0; i < inorder.size(); ++i) { index_map[inorder[i]] = i; } return buildHelper(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1, index_map); } private: TreeNode* buildHelper( vector<int>& preorder, int pre_start, int pre_end, vector<int>& inorder, int in_start, int in_end, unordered_map<int, int>& index_map) { // 终止条件:区间无效时返回空指针 if (pre_start > pre_end || in_start > in_end) return nullptr; // 前的第一个元素是当前根节点 int root_val = preorder[pre_start]; TreeNode* root = new TreeNode(root_val); // 获取根节点在中中的位置 int root_index = index_map[root_val]; // 计算左子树的节点数量(用于分割前序列) int left_size = root_index - in_start; // 递归构建左子树 root->left = buildHelper( preorder, pre_start + 1, pre_start + left_size, // 左子树前区间 inorder, in_start, root_index - 1, // 左子树中区间 index_map ); // 递归构建右子树 root->right = buildHelper( preorder, pre_start + left_size + 1, pre_end, // 右子树前区间 inorder, root_index + 1, in_end, // 右子树中区间 index_map ); return root; } }; ``` --- ### **3. 示例分析** 以输入为例: - **前序遍历**:`[3,9,20,15,7]` - **中序遍历**:`[9,3,15,20,7]` **构建过程**: 1. **根节点**:前第一个元素 `3`。 2. **分割中**: - 左子树中:`[9]`(根 `3` 左侧)。 - 右子树中:`[15,20,7]`(根 `3` 右侧)。 3. **递归构建**: - **左子树**:前 `[9]`,中 `[9]` → 直接创建节点 `9`。 - **右子树**:前 `[20,15,7]`,中 `[15,20,7]`: - 根节点 `20`。 - 左子树中 `[15]`,右子树中 `[7]`。 - 递归创建左子节点 `15` 和右子节点 `7`。 最终二叉树结构: ``` 3 / \ 9 20 / \ 15 7 ``` --- ### **4. 关键点解释** - **哈希表优化**:通过 `unordered_map` 快速定位中根节点位置,时间复杂度降至 $O(n)$。 - **区间分割**: - 前左子树区间:`pre_start + 1` 到 `pre_start + left_size`。 - 前右子树区间:`pre_start + left_size + 1` 到 `pre_end`。 - **终止条件**:当前或中区间无效时(起始索引超过结束索引),返回 `nullptr`。 --- ### **5. 复杂度分析** - **时间复杂度**:$O(n)$,每个节点仅访问一次。 - **空间复杂度**:$O(n)$,哈希表存储中索引,递归栈深度最坏为 $O(n)$(树退化为链表)。 --- ### **6. 扩展思考** - **后序遍历构建**:若已知中和后,方法类似,但根节点从后末尾取。 - **重复元素处理**:当节点值重复时,哈希表无法唯一定位,需其他策略(如上下文分析)。 掌握此方法可解决 LeetCode 题目 [105. 从前序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值