《剑指Offer》面试题:重构二叉树

题目描述:
输入某二叉树的前序遍历和中序遍历的结果,
请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并输出它的后序遍历序列。

思路
根据前序遍历的数组,arr[0]为根节点,在中序遍历中找到值等于arr[0]的位置index,则index的左边为此节点的左子树,右边为此节点的右子树。于是递归构建该节点的左右子树。

需要考虑的测试用例
1)一个结点的数
2)只有左子树的树
3)只有右子树的树
4)普通的树
5)特殊输入



/*
输入:
输入可能包含多个测试样例,对于每个测试案例,

输入的第一行为一个整数n(1<=n<=1000):代表二叉树的节点个数。

输入的第二行包括n个整数(其中每个元素a的范围为(1<=a<=1000)):代表二叉树的前序遍历序列。

输入的第三行包括n个整数(其中每个元素a的范围为(1<=a<=1000)):代表二叉树的中序遍历序列。

输出:
对应每个测试案例,输出一行:

如果题目中所给的前序和中序遍历序列能构成一棵二叉树,则输出n个整数,代表二叉树的后序遍历序列,每个元素后面都有空格。

如果题目中所给的前序和中序遍历序列不能构成一棵二叉树,则输出”No”。

样例输入:
8
1 2 4 7 3 5 6 8
4 7 2 1 5 3 8 6
8
1 2 4 7 3 5 6 8
4 1 2 7 5 3 8 6
样例输出:
7 4 2 5 8 6 3 1 No

*/ 


#include<stdio.h>
#include<stdlib.h>
struct TreeNode{
    int mValue;
    struct TreeNode *lchild;
    struct TreeNode *rchild;
};
bool canRebuild;
/*
函数的功能:根据树的前序遍历、中序遍历来重构二叉树。
参数的说明
@param pRoot:即将重构的Tree的头结点的指针。
@param n:节点的个数
@param  preTraversal :前序遍历的数组。 
@param inTraversal:后序遍历的数组 
*/

void  rebuildBTree(TreeNode **pRoot,int len,int *preTraversal,int *inTraversal){
    if(preTraversal==NULL||inTraversal==NULL) {
        canRebuild=false;
        return ;
    }
    //
    if(len<1){
        return;
    }

    //在中序遍历中找到前序遍历的头结点的左右子结点
    int index=-1; 
    for(int i=0;i<len;i++){
        if(preTraversal[0]==inTraversal[i]){
            index=i;
            break;
        }
    } 
    if(index==-1){//这种情况就是没有找到当前根结点在中序遍历的位置。因此不能重构 
        canRebuild=false;
        return;         
    }
    //找到了之后就开始构建此结点
    //为当前结点分配空间 
    (*pRoot)=(TreeNode *)malloc(sizeof(struct TreeNode));
    if((*pRoot)==NULL){
        exit(EXIT_FAILURE);
    }
    (*pRoot)->mValue=preTraversal[0];
    (*pRoot)->lchild=NULL;
    (*pRoot)->rchild=NULL;
    //接下来开始构建该结点的左右子树。 
    rebuildBTree(&((*pRoot)->lchild),index,preTraversal+1,inTraversal);
    rebuildBTree(&((*pRoot)->rchild),len-index-1,preTraversal+index+1,inTraversal+index+1); 


}
//按后序遍历输出二叉树 
void postTraversal(TreeNode *pRoot){
    if(pRoot==NULL){
        return;
    }
    if(pRoot->lchild!=NULL){
        postTraversal(pRoot->lchild);
    }
    if(pRoot->rchild!=NULL){
        postTraversal(pRoot->rchild);
    }
    printf("%d ",pRoot->mValue);
}
void destoryBTree(TreeNode* pRoot){
    if(pRoot==NULL){
        return;
    }
    destoryBTree(pRoot->lchild);
    destoryBTree(pRoot->rchild);
    if(pRoot->lchild==NULL||pRoot->rchild==NULL){
        free(pRoot);
        pRoot=NULL;
    }
    return;

}
int main(void){

    int n;
    while(scanf("%d",&n)&&n>0){
        int *preTraversal=(int *)malloc(n*sizeof(int));
        int *inTraversal=(int *)malloc(n*sizeof(int));
        if(preTraversal==NULL||inTraversal==NULL){
            exit(EXIT_FAILURE);
        }
        for(int i=0;i<n;i++){
            scanf("%d",preTraversal+i);
        }
        for(int i=0;i<n;i++){
            scanf("%d",inTraversal+i);
        }
        //重构二叉树
        TreeNode *pRoot;
        bool canRebuild=true;
        rebuildBTree(&pRoot,n,preTraversal,inTraversal) ;
        if(canRebuild){
            //按后序遍历输出
            postTraversal(pRoot);
            printf("\n");

        }
        else{
            printf("No\n");
        }
        //最后销毁二叉树
        destoryBTree(pRoot);
        free(preTraversal);
        preTraversal=NULL;
        free(inTraversal);
        inTraversal=NULL; 

    }
    return 0;
} 

今天和同学聊起这个题,说不知道用Java如何来重建二叉树,于是就写了下,代码如下,2016年8月23日16:20:20

public class RebuildTree2 {
    private static boolean canRebuild = true;
    private static int[] postOrder ;
    private static int indexPost = 0;
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre==null||in==null||pre.length!=in.length){
            return null;
        }
        TreeNode head = rebuildHelper(pre,in,pre.length);
        if(!canRebuild){//不能重建
            return null;
        }
        return head;
    }

    private TreeNode rebuildHelper(int[] pre, int[] in, int len) {
        if(pre==null||in==null||len<1){
            //canRebuild = false;
            return null;
        }
        int val = pre[0];//根节点
        //在中序遍历中找到头结点出现的位置
        int index = -1;
        for(int i=0;i<len;i++){
            if(in[i]==val){
                index = i;
                break;
            }
        }
        if(index==-1){//在中序遍历中没有找到根节点,因此不能重建
            canRebuild = false;
            return null;
        }
        //构建当前根节点
        TreeNode head = new TreeNode(val);
        //接着开始构建根节点的左右子树
        //左子树
        //先进行数组的拷贝
        int len1 = index ;
        int[] preNew = new int[len1];
        int[] inNew = new int[len1];
        for(int i=0;i<len1;i++){
            preNew[i] = pre[1+i];
            inNew[i] = in[i];
        }

        head.left = rebuildHelper(preNew, inNew,len1);
        //检查左子树是否能够重建
        if(!canRebuild){
            return null;
        }
        //右子树
        //先进行数组的拷贝
        int len2 = len-index-1;
        int[] preNew2 = new int[len2];
        int[] inNew2 = new int[len2];
        for(int i=0;i<len2;i++){
            preNew2[i] = pre[index+1+i];
            inNew2[i] = in[index+1+i];
        }
        head.right  = rebuildHelper(preNew2, inNew2,len2);
        return head;
    }

    public static void main(String[] args){
        int len = 7;
        int[] pre = {1,2,3,4,5,6,7};
        int[] in = {3,2,4,1,6,5,7};
        postOrder = new int[len];
        RebuildTree2 bt = new RebuildTree2();
        TreeNode head = bt.reConstructBinaryTree(pre,in);
        System.out.println(head.val);
        //后序遍历
        bt.postOrder(head); 
        for(int i=0;i<len;i++){
            System.out.print(postOrder[i]+" ");
        }
    }

    private  void postOrder(TreeNode head) {
        if(head==null){
            return ;
        }
        if(head.left!=null){
            postOrder(head.left);
        }
        if(head.right!=null){
            postOrder(head.right);
        }
        postOrder[indexPost] = head.val;
        indexPost++;
        //System.out.print(head.val+" ");
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值