点击蓝字 关注灯珑
二叉树的重建
前面几篇笔记讲了二叉树的表达与遍历。那么,有没可能根据二叉树遍历的结果,来重建出一棵二叉树呢?答案是肯定的。
给出二叉树前序遍历的结果和中序遍历的结果,我们就能根据这些信息,重新生成二叉树。这个问题相对来说有挑战性,需要花费更长的时间来思考。
看下面这棵树:
前序遍历结果为pre={1,2,3,4,5,6,7,8,9}
中序遍历结果为in={3,2,5,4,6,1,8,7,9}
我们可以发现,设前序遍历的当前节点为c,则在中序遍历的结果中,c点左侧和右侧就可以构成左子树和右子树。也就是说,把中序结果分成两半了。比如,前序当前为1时,那么中序的3,2,5,4,6都属于以1为父节点的左子树,7,8,9都属于以1为父结点的右子树。以此类推,我们就可以通过递归来重建整棵二叉树。
注意事项
我们在处理递归问题的时候,需要处理好区间边界的问题。也就是说,要明确我们的区间是开区间还是闭区间。不然很有可能就因为这区间的问题而导致程序出错。
而且,临界问题也容易出错。在这个问题里面就是结点的左子结点为空和右子结点为空的情况。所以,我在下面的程序里,用了两个标记变量去标记他们的左右子结点是否为空,为空则把它们指向NIL。
代码实现
#include#include#include#includeusing namespace std;#define MAX 100#define NIL -1struct Node{ int id; int left; int right; bool is_left_null; bool is_right_null;};Node T[MAX];int n;vector<int> st,mi;int pos=0;Node reconstruct(int left,int right){ int root = st[pos]; if(left >= right) { Node tmp; tmp.left = NIL; tmp.right = NIL; tmp.id = root; //处理好边界问题,也就是子节点为空的时候 tmp.is_left_null = true; tmp.is_right_null = true; return tmp; } pos++; int m = distance(mi.begin(), find(mi.begin(), mi.end(), root)); Node tmp1 = reconstruct(left,m); Node tmp2 = reconstruct(m+1,right); if(tmp1.is_left_null) { T[root].left = NIL; } else{ T[root].left = tmp1.id; } if(tmp2.is_right_null) { T[root].right = NIL; } else{ T[root].right = tmp2.id; } T[root].id = root; return T[root];}/*void check(){ for(int i=1;i<=n;i++) { if(T[i].left==T[i].right) { T[i].left = NIL; T[i].right = NIL; } if(T[i].left==0) T[i].left = NIL; if(T[i].right == 0) T[i].right = NIL; }}*/void postOrder(int u){ if(u==NIL) { return; } postOrder(T[u].left); postOrder(T[u].right); printf("%d", u); if(u!=st[0]) cout<<" ";}int main(){ for(int i=0;i { T[i].left = NIL; T[i].id = NIL; T[i].right = NIL; } cin>>n; for(int i=0;i { int tt; cin>>tt; st.push_back(tt); } for(int i=0;i { int tt; cin>>tt; mi.push_back(tt); } reconstruct(0,n); //check(); for(int i=1;i<=n;i++) { cout<<"id: "<" left: "< } cout<<endl; postOrder(st[0]); cout<<endl;}