前言

二叉树的遍历有三种方式,前序,中序和后序遍历都可以完成对二叉树的遍历。遍历一颗树很容易,那么建树呢?是否可以选择任意 2 种遍历序列来建立一颗二叉树。

分析

这三种遍历方式最为特殊就是中序遍历,中序遍历先遍历左子树后输出当前节点再遍历右子树,这种遍历方式可以保证当前位置的左边元素都是左子树的,右边的元素都是右子树的。前序序列的第一个元素为根节点,后序遍历的最后一个元素为根节点,配合中序遍历我们可以不断确定根节点从而建立二叉树,所以中序遍历配合后序或者前序就可以确认一颗二叉树,

那么前序和后序遍历配合能否建立二叉树呢?当然可以建立,不过这个二叉树无法唯一确定,因为前序和后序本质上都是将父节点和子节点进行分离,并没有指明左子树和右子树的能力。如果没有中序遍历来确定下一个根节点的方向,就可能出现下面这种情况:

上面两个二叉树的前序遍历都是2-1-3,后序遍历都是3-1-2,但是它们显然是不同的二叉树,所以根据先序序列和后序序列并不能唯一确定二叉树。(可以建树,不过结果可能不唯一)

实现

1、前序遍历和中序遍历建树

  • 前序遍历数组的第一个元素就是当前二叉树的根节点
  • 在中序序列找到该根节点,该根节点把中序序列分为左右子树两部分,确定左右子树长度
  • 通过左右子树长度来确定左右子树的前序序列范围,前部分是左子树的前序序列,后部分是右子树的前序序列
  • 同样的规则递归左右两个子树不断确定根节点

例如:

先序序列:1 2 3 4 5 6
中序序列:3 2 4 1 6 5 7

根据先序序列可以知道该根节点为1,那么找到中序序列中该根节点的位置 k,当前 k 为4 (1 2 3 4 5 6 7)
根据中序序列的特点,可知该位置的左边就是根节点的左子树,右边为根节点的右子树。(3 2 4 1 6 5 7)
接下来就要确定左右子树的前序和中序序列,不断缩小树的规模。

左子树的中序序列:ilk-1
左子树的先序序列:pl+1k-il+pl
右子树的中序序列:k+1ir
右子树的先序序列:k-il+pl+1pr

通过这些序列后左子树和右子树就是一颗我们原来的树了,以同样的方法递归左右子树不断确定根节点就建立了一颗树

Code~

#include<bits/stdc++.h>
using namespace std;
int a[1005],b[1005];
struct Tree{Tree * l, *r;int val;
};
// [pl,pr] 前序序列开始和结束
// [il,ir] 中序序列开始和结束
// n       树的结点个数
Tree * build(int pl,int pr,int il,int ir,int n){if(n == 0) return NULL;Tree * root = new Tree();root->val = a[pl];int k = il;while(a[pl] != b[k]) k++;root->l = build(pl + 1, k - il + pl, il, k - 1, k - il);root->r = build(k - il + pl + 1, pr, k + 1, ir, ir - k);return root;
}
int main()
{int n;cin >> n;for(int i = 1; i <= n; i++){cin >> a[i];}for(int i = 1; i <= n; i++){cin >> b[i];}Tree * tree;tree = build(1,n,1,n,n);
}

2、后序遍历和后序遍历建树

  • 后序遍历数组的最后一个元素就是当前二叉树的根节点

  • 在中序序列找到该根节点,该根节点把中序序列分为左右子树两部分,确定左右子树长度

  • 通过左右子树长度来确定左右子树的后序序列范围,前部分是左子树的后序序列,后部分是右子树的后序序列

  • 同样的规则递归左右两个子树不断确定根节点

左子树的中序序列:ilk-1
左子树的后序序列:plk-il+pl-1
右子树的中序序列:k+1ir
右子树的后序序列:k-il+plpr-1

同理递归左右子树不断确定根节点就建立了一颗树

#include<bits/stdc++.h>
using namespace std;
int a[1005],b[1005];
struct Tree{Tree * l,*r;int val;
};
// [pl,pr] 后序序列开始和结束
// [il,ir] 中序序列开始和结束
// n       树的结点个数
Tree * build(int pl,int pr,int il,int ir,int n){if(n == 0) return NULL;Tree * root = new Tree();root->val = a[pr];int k = il;while(a[pr] != b[k]) k++;root->l = build(pl, k - il + pl - 1, il, k - 1, k - il);root->r = build(k - il + pl, pr - 1, k + 1, ir, ir - k);return root;
}
int main()
{int n;cin >> n;for(int i = 1; i <= n; i++){cin >> a[i];}for(int i = 1; i <= n; i++){cin >> b[i];}Tree * tree;tree = build(1,n,1,n,n);
}

3、前序遍历和后序遍历建树

  • 前序遍历,根节点在左右子树的前前面。而在后序遍历中,根结点在左右子树后面。

  • 后序序列确定好根结点,该结点前面的序列按左子树右子树的顺序进行排列。

  • 在前序序列以一个顶点作为根节点,提供其随后的节点A作为左子树根节点

  • 在后序序列中找到该根节点A的位置,即可获取左子树的长度

  • 确定好左右子树的前序和后序序列,即可以同样的规则递归左右两个子树不断确定根节点。

左子树的前序序列:pl+1pl+k-il+1
左子树的后序序列:plk
右子树的前序序列:pl+k-il+1+1pr
右子树的后序序列:k+1ir-1
#include<bits/stdc++.h>
using namespace std;
struct node {int value;node *lchild, *rchild;
};
int n;
int pre[105], post[105];
map<int, int> mp;node *create(int pl, int pr, int il, int ir) {if (pl > pr) return NULL;if (pl == pr) {node* root = new node;root->value = pre[pl];root->lchild = root->rchild = NULL;return root;}node *root = new node;root->value = pre[pl];int k = mp[pre[pl + 1]];int numleft = k - il + 1;root->lchild = create(pl + 1, pl + numleft, il, k);root->rchild = create(pl + numleft + 1, pr, k + 1, ir - 1);return root;
}int main()
{cin >> n;for (int i = 0; i < n; i++){cin >> pre[i];}for (int i = 0; i < n; i++) { cin >> post[i]; mp[post[i]] = i; }node *root = create(0, n - 1, 0, n - 1);return 0;
}

前序和后序建树练习题:https://pintia.cn/problem-sets/994805342720868352/problems/99480535347086

参考资料:

https://blog.csdn.net/Mr_dimple/article/details/117046959 先序(后序)配合中序建树

https://blog.csdn.net/JasonRaySHD/article/details/104223642 前序序列与后序序列建树

是否能任取前序、中序和后序两种序列建立二叉树?相关推荐

  1. java如何获得键值_如何在java中取map中的键值 的两种方法

    第一种方法根据键值的名字取值 import java.util.HashMap; import java.util.Map; public class Test { /** * @param args ...

  2. JAVA中如何取map的值_如何在java中取map中的键值 的两种方法

    第一种方法根据键值的名字取值 import java.util.HashMap; import java.util.Map; public class Test { /** * @param args ...

  3. 二叉树深度优先 java_二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历) java实现...

    二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的.对于二叉树,有深度遍历和广度遍历,深度遍历有前序.中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历.由于树的 ...

  4. leetcode 106. 从中序与后序遍历序列构造二叉树 105. 从前序与中序遍历序列构造二叉树思考分析

    目录 1.106题目 2.参考思路:递归切割数组 3.105题目 4.同样思路的代码 1.106题目 2.参考思路:递归切割数组 代码参考:公众号:代码随想录 后序数组+中序数组 以 后序数组(左右中 ...

  5. 【LeetCode笔记】94 144 145. 二叉树的前序、中序、后序遍历的迭代与递归(Java、dfs、迭代)

    文章目录 一. 题目描述 二. 代码 & 思路 1. 递归的写法 2. 迭代的写法(本文重点来了) 1) 前序 2) 中序 3) 后序 直接来个整合吧,也方便看.之前只写了递归的,现在补上迭代 ...

  6. 二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)

    二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的.对于二叉树,有深度遍历和广度遍历,深度遍历有前序.中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历.由于树的 ...

  7. 【必拿下系列】106. 从中序与后序遍历序列构造二叉树105从前序与中序遍历序列构造二叉树

    两题各自的链接放这里了: 链接: 106 链接: 105 106.从中序与后序遍历序列构造二叉树 如果你是不知道理论的,那就得仔细分析了, 举个例子: 输入:inorder = [9,3,15,20, ...

  8. 树的基本概念和遍历规则 数据结构和算法 二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)

    zsychanpin 博客园 首页 新随笔 联系 订阅 管理 树的基本概念和遍历规则 树的递归定义 树是n(n>0)个结点的有限集,这个集合满足下面条件:       ⑴有且仅有一个结点没有前驱 ...

  9. 非递归实现二叉树的前序、中序、后序遍历

    目录 非递归实现二叉树的前序遍历 非递归实现二叉树的中序遍历 非递归实现二叉树的后序遍历 根据二叉树的前序和中序遍历结果还原二叉树 根据二叉树的中序和后序遍历结果还原二叉树 非递归遍历需要借助栈. 非 ...

最新文章

  1. 吴恩达卷积神经网络课程——第一周笔记
  2. HTTP 错误 404.2 - Not Found
  3. 实战项目 10: 货物清单应用
  4. 【干货】接地气的产品设计流程
  5. android realmax sdk,RealMax推出全新开源AR SDK 框架ARToolKit
  6. Android Realm相关操作
  7. Rust从入门到放弃(1)—— hello,world
  8. php 异常 重试,Python中异常重试的解决方案详解
  9. python实时处理log文件脚本
  10. 《KAFKA官方文档》入门指南(二)
  11. html求相关系数,皮尔森相关系数怎么看 相关系数多少算具有相关性?
  12. html控制word打印在一张页面,HTML文件到WORD文档双面打印三步曲
  13. Kubernetes crictl管理命令详解
  14. 编辑表格用什么软件比较好?最好用的都在这了!
  15. html2:什么是超文本?
  16. 马云:2019年两大行业,会成就一批千万富翁!
  17. github上三个不错的开源框架
  18. 阿里云Redis开发规范学习总结
  19. ACM-SIAM离散算法研讨会SODA 2020今日召开
  20. 部署以太坊智能合约01

热门文章

  1. 《Fooled by Randomness》读书笔记
  2. Webgl实现的天气效果(下雨、下雪)
  3. 常见的三种字符编码ASCII、Unicode、UTF-8
  4. 生活 or 办公——怎么能少了 PeakDo 毫米波无线投屏器呢
  5. android绘制虚线
  6. Xamarin.Android之绑定库教程
  7. 去年净亏7.37亿美元,“自动驾驶第一股”的商业化之痛
  8. 怎么用免费视频引流?如何利用视频网站免费引流?
  9. CAD直接打印,不出现打印对话框
  10. 关于Unity ContentSizeFitter的坑