首页 > 其他 > 详细

由中序和先序(后序)非递归重建二叉树 时间复杂度O(n)

时间:2021-06-06 22:34:11      阅读:28      评论:0      收藏:0      [点我收藏+]

先序和中序参考另一篇文章https://blog.csdn.net/qq_54846814/article/details/117489504

和由前序中序推重建树类似

输入:

第一行一个整数n,表示节点编号1~n。
第二行n个整数,表示后序遍历。
第三行n个整数,表示中序遍历。
算法过程:
post[n]为后序序列 in[n]为中序序列 vis[n]记录是否已在后序序列中出现

初始两个下标cur1=n-1,cur2=n-1
1.当post[cur1]!=in[cur2]时 cur1往后退一格,并且post[cur1]号结点的右孩子为post[cur1-1]号结点 并记录vis[post[cur-1]]=1。

while (in[cur2] != post[cur1]) {
tree[post[cur1]].right = post[cur1 - 1];
vis[post[cur1 - 1]] = 1;
cur1--;
}

2.此时post[cur1]==in[cur2],我们让cur2后退,直到in[cur2]号结点未出现过,即vis[in[cur2]]=0

while (vis[in[cur2]]) {
cur2--;
}

3.这时令in[cur2+1]号结点的左孩子为pre[cur1-1],cur1后退一格

tree[in[cur2 + 1]].left = post[cur1 - 1];
vis[post[cur1 - 1]] = 1;
cur1--;

重复1,2,3 直至cur1或cur2走到0。

下面演示一组例子执行过程 

绿色代表中序数组的进展 黄色代表通过步骤3连结的结点

技术分享图片 

注意在最后一张图片中 后序数组已经走到起点 即cur1已经为0(中序数组没有走完)


下面证明

每次中序序列后退时第一个未在后序序列中出现的节点的后一个结点(cur2+1)是后序序列中下一个结点(cur1-1)的父亲(父亲和左孩子)
 

由于该点的后一个点(cur2+1)在已经遍历过的后序序列中出现,那么因为中序遍历的性质,后一个点的右子树上任一个点必在该之后被遍历到,即后一点右子树上任意一点已在后序序列中出现过,并且由中序遍历性质还可得到该点为后一个点的左子树上的结点(反证法:显然该点不可能在前一点的右子树,如果为前一点的父亲/长辈结点,那么该点一定在后序序列中出现过 从而产生矛盾,这是因为后序遍历任何一个结点必在其子结点后被遍历到)而且为左子树上最右的节点。那么因为在后序序列中(从后往前)左子树上最右的结点都没有出现过,从而整棵左子树上的结点都没有出现过(反过来就是都已被遍历完),综上:
1.后一个结点(cur2+1)的右子树在后序遍历中未被遍历过
2.后一个结点(cur2+1)的左子树在后序遍历中已遍历完  (注意这里遍历完的意思是从前往后的顺序已经遍历完)
那么可以得出结论中序序列后一个点的左孩子就是后序遍历序列中的下一个结点(从后往前)。
这里多解释一下,后一个节点的左子树的根节点在后序遍历中一定最后被遍历到,那么在序列中的顺序就是从后往前的下一个结点。

复杂度分析:显然这里cur1,cur2不会回退时间复杂度O(n);
其次,对于无法通过过序列的值访问结点的情况,即步骤二无法直接访问到对应结点的情况,可通过哈希方法记录对应序列的值和结点的指针,这样来时间复杂度仍是O(n)的。

#include<bits/stdc++.h>
using namespace std;
int    n;
struct bitnode {
    int left, right;
    bitnode() :left(0), right(0) {}
};
const int maxn = 1e5 + 5;
int post[maxn], in[maxn], vis[maxn];
int root;
bitnode tree[maxn];
void solve() {
    root = post[n-1];
    vis[post[n-1]] = 1;
    int cur1 = n-1, cur2 = n-1;
    while (cur1 > 0 || cur2 > 0) {
        //当post[cur1]!=in[cur2]时,cur1往后退一格
        //并且post[cur1]号结点的左孩子为post[cur1-1]号结点,并记录vis[post[cur-1]]=1。
        while (in[cur2] != post[cur1]) {
            tree[post[cur1]].right = post[cur1 - 1];
            vis[post[cur1 - 1]] = 1;
            cur1--;
        }
        //此时post[cur1]==in[cur2],我们让cur2后退
        //直到in[cur2]号结点未出现过,即vis[in[cur2]]=0
        while (vis[in[cur2]]) {
            cur2--;
        }
        //这时令in[cur2+1]号结点右孩子为pre[cur1-1],cur1后退一格 
        tree[in[cur2 + 1]].left = post[cur1 - 1];
        vis[post[cur1 - 1]] = 1;
        cur1--;
    }
}
void preorder(int root)
{
    cout << root << " ";
    if (tree[root].left) {
        preorder(tree[root].left);
    }
    if (tree[root].right) {
        preorder(tree[root].right);
    }
}
/*
9
5 4 2 6 8 9 7 3 1
4 5 2 1 6 3 8 7 9
*/
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> post[i];
    }
    for (int i = 0; i < n; i++) {
        cin >> in[i];
    }
    clock_t s = clock();
    solve();
    clock_t e = clock();
    //cout << e - s;
    preorder(root);
    return 0;
}

 

由中序和先序(后序)非递归重建二叉树 时间复杂度O(n)

原文:https://www.cnblogs.com/4759liu/p/14856305.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!