学习笔记——已知二叉树的先序中序求后序、中序后序求先序的遍历序列

2013.10.21 早上八点

问题:二叉树已知先序中序求后序遍历序列问题

问题描述:

已知二叉树的先序遍历

先序遍历: 

   1.访问根节点 

   2.前序遍历左子树 

   3.前序遍历右子树 

已知二叉树的中序遍历

中序遍历:

1.中序遍历左子树 

   2.访问根节点 

   3.中序遍历右子树

 求二叉树的额后序遍历序列:

1.后序遍历左子树 

   2.后序遍历右子树 

   3.访问根节点

 

比如:输入的先序序列为:” GDAFEMHZ”,中序序列为:” ADEFGHMZ”,其后序遍历序列为”AEFDHZMG”

 

大致思路:

1 二叉树的遍历

         前序遍历:先遍历根节点,然后遍历左子树,然后遍历右子树

         中序遍历:先遍历左子树,然后遍历根节点,然后遍历右子树

         后序遍历:先遍历左子树,然后遍历右子树,最后遍历根节点

         举例如下:

         

         前序遍历:a b d e c f g   中序遍历:d b e ag f c  后序遍历:d e b g f c a

2 已知两种遍历序列求第三种遍历序列

<1>已知前序和中序求后序遍历

           拿上图举例根据前序序列:a b d e c f g   中序序列:d b e ag f c  由于前序序列先访问根节点,所以先序

序列的第一个就是二叉树的根节点,然后看中序序列中根节点a,他的左半部分(dbe)为他的左子树,(gfc)为他

的右子树。前序序列第二个访问b,中序序列可以判定出d为b的左子树,e为b的右子树。根据前序序列知道c为根节

点,根据中序得知c没右子树,只含左子树。同理最后可判定f为根节点,g为其左子树。至此可得出二叉树的图,

正如上图所示。即可写出后序序列:d e b g f c

<2>已知前序和后序求中序遍历

        这样是不能得到一个唯一的二叉树出来滴举例如下:

        树A:有结点1,2,3,2是1的左孩子,3是2的右孩子

        树B:有结点1,2,3,2是1的右孩子,3是2的左孩子

 

        则A,B的前序都是123,后序都是321,但A的中序是231,B的中序是132

<3>已知中序序列和后序序列求先序序列

        依旧拿上图举例,已知中序序列:d b e a g f c 后序序列:d e b g f c a。由后序遍历序列定义得知最后一个

节点a必是树的根节点,由中序序列得知左子树(dbe),右子树(gfc),由后序序列倒数第二个为c,得知右子树根

节点为c,再由中序序列可判断出c只含左子树,由后序倒数第三个f判断出f为根节点,再由中序判断出f含左孩子

g。同理可依次判断左半部分(dbe)。至此可画出二叉树。

 

 

代码:(已知先序中序求后序) 参考至:http://functionghw.is-programmer.com/posts/38896.html

#ifndef_PREIN2POSTORDER_H_

#define_PREIN2POSTPRDER_H_

 

#include<iostream>

usingnamespace std;

 

structTreeNode

{

    TreeNode* m_pLeft;

    TreeNode* m_pRight;

    char m_cValue;

};

 

//按后序遍历的顺利释放树的节点

voiddelete_tree(TreeNode* root)

{

    if (root!=NULL)

    {

        delete_tree(root->m_pLeft);

        delete_tree(root->m_pRight);

        free(root);

    }

}

 

 

 

 

//输出后序遍历的结果

voidpost_order_print(TreeNode* pNode)

{

    if (pNode!=NULL)

    {

        post_order_print(pNode->m_pLeft);

        post_order_print(pNode->m_pRight);

        printf("%c",pNode->m_cValue);

    }

}

 

//线性搜索函数,返回valinorder中的位置

intindex_at_inseq(char* in_order_seq, int len, char val)

{

    int i=0;

    for (;i<len;++i)

    {

        if (*(in_order_seq+i)==val)

        {

            return i;

        }

    }

    return -1;

}

 

//递归构造二叉树,最后一个参数用于指示构造是否成功

TreeNode*ConstructBTree(char* in_order_seq, char* pre_order_seq, int len, bool*is_success)

{

    if (len<=0 || (*is_success)==false) //空树或者构造过程出错

    {

        return NULL;

    }

 

    int root_index=index_at_inseq(in_order_seq,len, *pre_order_seq);

    if (root_index<0)   //根节点的值不匹配,停止构造

    {

        (*is_success)=false;

        fprintf(stderr,"wrongsequences.\n");

        return NULL;

    }

 

    TreeNode*root=(TreeNode*)malloc(sizeof(TreeNode));

    if (root==NULL)     //内存不足,无法分配内存

    {

        (*is_success)=false;

        fprintf(stderr,"out ofmemory.\n");

        return NULL;

    }

 

    root->m_cValue=*pre_order_seq;

 

    int left_subtree_len=root_index;

    int rigth_subtree_len=len-(root_index+1);

 

    //左子树

    root->m_pLeft=ConstructBTree(in_order_seq,pre_order_seq+1,left_subtree_len,is_success);

 

    //右子树

root->m_pRight=ConstructBTree(in_order_seq+root_index+1,pre_order_seq+root_index+1,rigth_subtree_len,is_success);

 

    return root;

}

 

//使用时应该使用这个函数,如果构造失败,需要清理内存.

TreeNode*GetBTree(char* in_order_seq, char* pre_order_seq, int len)

{

    bool is_success=true;

    TreeNode*pNode=ConstructBTree(in_order_seq,pre_order_seq,len,&is_success);

    if (is_success)

    {

        return pNode;

    }else

    {//构造失败,清理已创建节点

        delete_tree(pNode);

        return NULL;

    }

 

}

 

int run()

{

 

    char* in_order_seq="ADEFGHMZ";

    char* pre_order_seq="GDAFEMHZ";

    int len1=strlen(in_order_seq);

    int len2=strlen(pre_order_seq);

    if (len1!=len2)

    {

        fprintf(stderr,"the len of thesequences is not equal.\n");

        return -1;

    }

 

    printf("the preorder is: ");

    printf("%s",pre_order_seq);

    printf("\n");

 

    printf("the inorder is: ");

    printf("%s",in_order_seq);

    printf("\n");

 

    TreeNode*TreeRoot=GetBTree(in_order_seq,pre_order_seq,len1);

    printf("the postorder is: ");

    post_order_print(TreeRoot);

    printf("\n");

    delete_tree(TreeRoot);

    system("pause");

 

    return 0;

}

 

#endif

样例运行结果:

代码:(已知中序后序求先序) 参考至:http://functionghw.is-programmer.com/posts/38896.html

#ifndef_INPOST2PREORDER_H_

#define_INPOST2PREORDER_H_

 

#include<iostream>

//#include<stdbool.h> //C99.

usingnamespace std;

 

typedefstruct TreeNode TreeNode;

 

structTreeNode {

    char val;

    TreeNode* left;

    TreeNode* right;

};

 

//按后序遍历顺序释放节点.

voiddelete_tree(TreeNode* root)

{

    if (root != NULL)

    {

        delete_tree(root->left);

        delete_tree(root->right);

        free(root);

    }

}

 

//输出先序遍历的结果

voidpre_order_print(TreeNode* pNode)

{

    if (pNode!=NULL)

    {

        printf("%c",pNode->val);

        pre_order_print(pNode->left);

        pre_order_print(pNode->right);

    }

}

 

 

 

//一个线性搜索函数,返回valin_order_seq[len]中的索引.

intindex_at_inseq(char* in_order_seq, int len, char val)

{

    int i;

    for (i = 0; i < len; ++i)

    {

        if(*(in_order_seq+i) == val) return i;

    }

    return -1;

}

 

//递归构造二叉树,最后一个参数用于指示构造是否成功.

TreeNode*ConstructBTree(char* in_order_seq, char* post_order_seq, int len, bool*is_success)

{

    if (len < 1 || (*is_success) == false)//空树或构造过程已经出错.

    {

        return NULL;

    }

 

    int root_index = index_at_inseq(in_order_seq,len, *(post_order_seq+len - 1));

    if (root_index < 0)//根节点的值不匹配,停止构造.

    {

        (*is_success) = false;

        fprintf(stderr, "Wrongsequences.\n");

        return NULL;

    }

 

    TreeNode* root =(TreeNode*)malloc(sizeof(TreeNode));

    if (root == NULL)//内存不足.

    {

        (*is_success) = false;

        fprintf(stderr, "Out ofmemory.\n");

        return NULL;

    }

    root->val = *(post_order_seq+len - 1);

 

    int left_subtree_len = root_index;

    int right_subtree_len = len -(left_subtree_len + 1);

 

    root->left = ConstructBTree(in_order_seq,

        post_order_seq,

        left_subtree_len,

        is_success);

 

    root->right = ConstructBTree(in_order_seq+ left_subtree_len + 1,

        post_order_seq + left_subtree_len,

        right_subtree_len,

        is_success);

    return root;

}

 

//使用时应该使用这个函数,如果构造失败,需要清理内存.

TreeNode*GetBTree(char* in_seq, char* post_seq, int len)

{

    bool is_success = true;

    TreeNode* p = ConstructBTree(in_seq,post_seq, len, &is_success);

    if (is_success)

    {

        return p;

    }

    else

    {//构造失败,清理所有已创建的节点.

        delete_tree(p);

        return NULL;

    }

}

 

int run()

{

    char* in_order_seq = "ADEFGHMZ";

    char* post_order_seq = "AEFDHZMG";

    int len1=strlen(in_order_seq);

    int len2=strlen(post_order_seq);

    if (len1!=len2)

    {

        fprintf(stderr,"the len of thesequences is not equal.\n");

        return -1;

    }

    printf("the inorder is: ");

    printf("%s",in_order_seq);

    printf("\n");

 

    printf("the postorder is: ");

    printf("%s",post_order_seq);

    printf("\n");

 

    TreeNode* theTree = GetBTree(in_order_seq,post_order_seq, len1);

 

    printf("the preorder is: ");

    pre_order_print(theTree);

    printf("\n");

    delete_tree(theTree);

 

    return 0;

}

#endif

样例运行结果:

 

 

问题关键点:掌握先序、中序、后序遍历的特点规律

资料参考至:

http://blog.youkuaiyun.com/fansongy/article/details/6798278?reload

http://www.oschina.net/code/snippet_577630_17173

http://functionghw.is-programmer.com/posts/38896.html

<think>好的,用户现在的问题是想要迅速掌握解题的思维方法,特别是面对力扣题目时。用户之前提到过在做滑动窗口等题目时遇到困难,经常需要看答案,感觉无法独立解题。现在他想知道如何快速培养这种思维能力。 首,我需要回顾之前的对话历史。之前我给出了分步骤的解决方案,包括拆解题目、科学刷题方法、模板思维模型等。用户现在可能已经尝试了这些方法,但进展不大,或者想更快见效。所以需要更具体的策略来加速思维能力的形成。 接下来,思考用户的需。用户提到“迅速掌握”,所以需要高效的方法,避免无效练习。但编程思维不可能一蹴而就,所以需要找到平衡点,既有效又不让人感到压力太大。可能的潜在需是用户希望减少挫败感,增加自信心,找到正确的练习路径。 用户可能遇到的问题包括:看到题目无从下手,无法将问题映射到已知算法,或者即使知道算法也无法正确实现。需要针对这些痛点提供具体的训练方法。 然后,我需要整理有效的策略。比如,分解问题、刻意练习、反馈循环、模式识别等。可能需要结合实例,让用户更容易理解如何应用这些方法。 同时,用户可能缺乏系统性的训练计划,需要给出具体的步骤或练习流程,比如每天如何安排时间,如何选择题目,如何总结等。此外,强调思维过程的记录,比如写解题日志,可能有助于用户反思提高。 另外,用户可能对算法模板的理解不够深入,只是机械记忆,而没有理解其适用场景变体。需要强调理解原理的重要性,而不仅仅是套用模板。 最后,要鼓励用户坚持,同时提供可操作的建议,避免过于理论化的内容。可能需要用例子来展示如何分解问题,如何从暴力解法优化到高效算法,以及如何避免常见错误。</think>要快速建立算法解题思维,关键在于建立「问题模式识别→算法映射→模板变形」的神经反射。以下是经过验证的高效训练框架: --- ### 一、建立模式识别直觉(2周专项训练) **1. 暴力解法 → 优化解法的映射训练** - 例:两数之问题 ```python # 暴力法 O(n²) for i in range(n): for j in range(i+1, n): if nums[i]+nums[j] == target: return [i,j] # 优化为哈希表 O(n) hashmap = {} for i,num in enumerate(nums): if target-num in hashmap: return [hashmap[target-num], i] hashmap[num] = i ``` **思维要点**:发现暴力法中重复计算的环节(反复遍历找补数),用空间换时间 **2. 特征信号速查表(高频题型触发条件)** | 出现以下关键词 | 立即联想到的算法 | 例题编号 | |-------------------------|------------------------|----------------| | 连续子数组/子串 | 滑动窗口 | 209, 3, 76 | | 有数组找目标 | 二分查找 | 33, 34, 540 | | 路径总数/最优解 | 动态规划 | 62, 64, 70 | | 所有组合/排列 | 回溯+剪枝 | 39, 46, 78 | | 最近邻/前K大 | 堆(优队列) | 215, 347, 703 | --- ### 二、算法变形能力强化(核心模板+变体训练) **以二分查找为例,掌握三大变形:** ```python # 标准模板 left, right = 0, len(nums)-1 while left <= right: mid = (left + right) // 2 if nums[mid] == target: return mid elif nums[mid] < target: left = mid + 1 else: right = mid - 1 # 变形1:找第一个≥target的位置 → 插入位置 # 变形2:找旋转排数组中的最小值 → 判断mid在左半还是右半区间 # 变形3:在无限序列中查找 → 动态扩展right边界 ``` **训练方法:** 1. 手写标准模板 2. 用同一模板解三道变形题(如34.找区间、153.旋转数组最小值、702.无限序列搜索) 3. 对比不同点,用红笔标注条件分支的变化 --- ### 三、构建解题决策树(遇到新题时快速判断) **决策流程图示例:** $$ \begin{cases} \text{是否有?} & → \text{二分查找/双指针} \\ \text{极值?} & → \text{动态规划/贪心} \\ \text{数据流处理?} & → \text{堆/单调队列} \\ \text{状态组合?} & → \text{回溯/位运算} \\ \text{图结构?} & → \text{BFS/DFS/拓扑排} \end{cases} $$ **实战演练:** 1. 拿到题目后,强制在1分钟内画出该决策树的分支 2. 例如「接雨水」问题: - 非有数组 → 排除二分 - 最大储水量 → 考虑动态规划或双指针 - 需要左右边界信息 → 选择双指针向中间逼近 --- ### 四、高频失误点规避训练 **常见思维漏洞及修正方法:** | 错误类型 | 典型表现 | 修正方案 | |-------------------------|------------------------|------------------------------| | 边界条件遗漏 | 空数组、单元素、极值 | 用测试用例:[], [0], [1], [INT_MAX] | | 更新顺错误 | 移动指针再计算结果 | 用箭头图逐步模拟执行流程 | | 数据结构选择失误 | 用列表代替哈希表 | 分析时间复杂度:查找O(1)→哈希 | | 循环终止条件错误 | while left < right 误用| 举例验证:当left==right时是否需处理 | --- ### 五、高效刷题节奏控制 **两周速成训练计划:** ``` 【第一周】夯实基础 早上:专题学习(1.5h)→ 看讲解视频+手写模板 下午:基础题实战(2h)→ 每个模板做3道同类简单题 晚上:错题重做(1h)→ 必须脱离IDE手写代码 【第二周】强化突破 早上:变形题训练(2h)→ 同一算法的不同变种题 下午:模拟面试(1.5h)→ 随机选题,限时30分钟/题 晚上:思维导图整理(1h)→ 归纳算法间的关联点 ``` **关键工具:** - 代码执行可视化:pythontutor.com - 手写解题日志模板: ``` 日期:______ 题目:______ 1. 初始思路(含错误尝试):__________ 2. 卡住点及突破方法:__________ 3. 时间复杂度分析:O( ) → 优化后O( ) 4. 同类题特征总结:__________ ``` --- ### 六、突破瓶颈的实战技巧 **当完全没思路时:** 1. **逆向推导法**:从输出样例反推处理步骤 - 例:「下一个排列」,手动计算几个例子: ``` 1,2,3 → 1,3,2 3,2,1 → 1,2,3 ``` 观察交换点反转规律 2. **降维攻击**:将难题转化为已知问题 - 例:「柱状图中最大矩形」→ 转化为「找每个柱子左右第一个比它矮的」(单调栈的应用) 3. **参数隔离法**:解决简化版问题 - 例:解决「二叉树最大深度」(简单),再进阶到「二叉树直径」(需记录左右深度) --- 坚持这种结构化训练,配合每日3-4小时的高效练习(重点在质量而非数量),大多数人在**30天左右**能显著提升独立解题能力。记住这个进步公式: $$ \text{思维突破} = \frac{\text{模式识别训练} \times \text{模板变形能力}}{\text{无效刷题次数}} + \text{及时反馈修正} $$ 最后送你我当年写在笔记本上的话:“每个看答案的题目,都是发现思维漏洞的契机——把困惑变成精确的问题,你就离正确答案更近一步。”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值