二叉树的重建

本篇博客主要讲解:如何已知前序后序重建二叉树,还有中序后序重建二叉树。

在讲解之前,先罗列一下二叉树的基本概念(已经会的同学可以跳过)。

二叉树定义:二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。然而,没有足够的信息来区分左结点和右结点。如果不考虑连通性,允许图中有多个连通分量,这样的结构叫做森林。

树的结点:包含一个数据元素及若干指向子树的分支;
孩子结点:结点的子树的根称为该结点的孩子;
双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;
兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点;
祖先结点: 从根到该结点的所经分支上的所有结点子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙
结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;
树的深度:树中最大的结点层
结点的度:结点子树的个数
树的度: 树中最大的结点度。
叶子结点:也叫终端结点,是度为 0 的结点;
分枝结点:度不为0的结点;
有序树:子树有序的树,如:家族树;
无序树:不考虑子树的顺序;

我们先建一颗完全二叉树
                   1
             2           3
       4        5   6      7
然后再了解一下四种遍历的二叉树的方法:分别为前序(先序),中序,后序,层序。
这里顺序是按根节点,左子树,右子树中根节点的遍历顺序
前序(先序):根节点->左子树->右子树 ,所以上面的遍历结果为:1 2 4 5 3 6 7
中序:左子树->根节点->右子树, 所以上面的遍历结果为:4 2 5 1 3 6 7
后序:左子树->右子树->根节点, 所以上面的遍历结果为:4 5 2 6 7 3 1
层序:这个比较特殊,是按照层的顺序输出的。 所以上面的遍历结果为:1 2 3 4 5 6 7

前序遍历代码:
preOrderParse(int n) {
    if(tree[n] == NULL)
        return ; // 如果这个节点不存在,那么结束 

    cout << tree[n].w ; // 输出当前节点内容     
    preOrderParse(tree[n].leftChild); // 递归输出左子树 
    preOrderParse(tree[n].rightChild); // 递归输出右子树 
} 

中序遍历代码:
inOrderParse(int n) {
    if(tree[n] == NULL)
        return ; // 如果这个节点不存在,那么结束 

    inOrderParse(tree[n].leftChild); // 递归输出左子树 
    cout << tree[n].w ; // 输出当前节点内容 
    inOrderParse(tree[n].rightChild); // 递归输出右子树 
} 
后序遍历代码:
pastOrderParse(int n) {
    if(tree[n] == NULL)
        return ; // 如果这个节点不存在,那么结束 

    pastOrderParse(tree[n].leftChild); // 递归输出左子树 
    pastOrderParse(tree[n].rightChild); // 递归输出右子树 
    cout << tree[n].w ; // 输出当前节点内容     
} 

我们发现以上三个都是通过递归完成的。但是层序遍历不一样。提到层。有没有想到bfs。没有想到没关系。层序遍历和bfs差不多。需要用到队列这个先进先出的数据结构。来维护层序。
具体大家可以看一下代码:
while(!que.empty())  {
    int n = que.front(); // 得到队头元素
    que.pop();  // 队头元素出队列 
    // 如果当前节点不为空,那么输出节点的数值,并且在队尾插入左右子节点
    if(tree[n] != NULL) {
        cout << tree[n].w;
        que.push(tree[n].leftChild); 
        que.push(tree[n].rightChild); 
    }
}

明白了这些基础知识点后 我们来看如何用前序中序,来重建一颗二叉树。
首先来看先序遍历的一些特点。由于先序遍历一定是先遍历到根节点。所以第一个数,一定是这棵树根的值。但是我们会发现就算知道了根是哪个,也不知道他的左右子树的详细情况。
如何才能确定根节点的左右子树的数量呢?
这时候我们再看一下中序遍历的特点。中序遍历会把根节点的所有左子树输出出来,再输出根节点,然后再输出右子树。简单来说一下,就是中序中根的左边是左子树,根的右边是右子树。
那么我们就可以去中序中确定根的左右子树的情况。
放一张博主偷来的图:
这就是大致思路。
看一下具体代码:
void rec(int l, int r, int n) { 
    if(l >= r) {
        node[n].w = INF; // 如果当前节点为空,那么赋值为 -1 
        return ;
    } 
    int root = pre[pos++];
    node[n].w = root; // 获取当前节点储存的值 
    node[n].l = 2 * n; // 当前节点左孩子所在数组下标 
    node[n].r = 2 * n + 1;  // 当前节点右孩子节点所在数组下标 
    int m = find(in, in+r, root) - in; // 得到当前节点在中序遍历数组中的下标
    rec(l, m, 2*n); // 重建左子树 
    rec(m+1, r, 2*n+1); // 重建右子树 
}

后序遍历和中序遍历重建是一个道理。因为后序遍历最后一个是根,所以用同样的思路一样的。就不多做解释,直接上代码:
void rec(int l, int r, int n) {
    if(l >= r) {
        node[n].w = INF; // 如果节点不存在,赋值为 INF 
        return ;
    }
    int root = post[pos--]; // 获取后续遍历中节点的数值 
    node[n].w = root;
    node[n].l = 2*n; // 左子树所在数组下标 
    node[n].r = 2*n+1; // 右子树所在数组下标 
    int m = find(in, in+r, root) - in;

    rec(m+1, r, 2*n+1); // 因为是后序遍历,所以先重建右子树 
    rec(l, m, 2*n); // 在重建左子树 
}

若还不懂可以私聊博主。或者看一下博主学习的网站:http://blog.csdn.net/hacker_zhidian/article/details/60770262。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值