1. 程式人生 > >(二叉樹)從前序與中序遍歷構造樹

(二叉樹)從前序與中序遍歷構造樹

題目描述

根據一棵樹的前序遍歷與中序遍歷構造二叉樹。
注意:
你可以假設樹中沒有重複的元素。
例如,給出
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
[3,9,20,null,null,15,7]

題目分析

詳細分析類比 https://blog.csdn.net/qq_28114615/article/details/85175874
前序遍歷的第一個即為根結點,
以給出的例子 中序遍歷 inorder = [9,3,15,20,7]和前序遍歷 preorder = [3,9,20,15,7]來分析:

由前序遍歷可以知道根結點為3,再來到中序遍歷中,那麼3的左邊和右邊就分別是根結點的左子樹和右子樹的中序遍歷結果,即根結點的左子樹中序遍歷為[9],右子樹中序遍歷為[15,20,7],很顯然,根結點的左子樹就只剩一個結點9了。

現在再來分析右子樹:前面根據中序遍歷可以知道,根結點的左子樹上只有1個結點,右子樹上有3個結點,結合前序遍歷是根-左-右的特點,即前序遍歷中必定是先遍歷根結點的左子樹,然後再是右子樹,絕不會出現交叉的現象,因此在前序遍歷結果中從後往前數3個,即是右子樹的前序遍歷結果,再接著數1個結點,即是左子樹的前序遍歷結果,最後剩下一個根結點,由此可以分析出:根結點為3,對於左子樹:中序遍歷為[9],前序遍歷為[9];對於右子樹,中序遍歷為[15,20,7],前序遍歷為[20,15,7],這樣就可以分別得到左子樹和右子樹的中序和前序遍歷結果,再如同上面分析即可。

那麼現在來總結一下思路:
在這裡插入圖片描述
如圖所示,先從前序遍歷的開頭出發,找到中序遍歷中根結點所在位置pos,那麼instart ~ pos-1則是左子樹的中序遍歷,pos+1 ~ inend則是右子樹的中序遍歷,同樣,prend-(inend-pos)+1 ~ prend則是右子樹的前序遍歷,prstart+1 ~ prend-(inend-pos)則是左子樹的前序遍歷,這樣,就最終找到左子樹和右子樹各自的的前序遍歷和後序遍歷,這樣一直遞迴下去,將instart和pos-1作為左子樹的instart和inend,pos+1和inend作為右子樹的instart和inend;
prstart+1和 prend-(inend-pos)作為左子樹的prstart和prend,prend-(inend-pos)+1 和prend作為右子樹的prstart和prend,可見,instart和inend、prstart和prend是在不斷靠近的,直到instart>inend時,說明左子樹為空,此時prstart必定大於prend,遞迴停止,寫出程式如下:

 TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(inorder.empty())return NULL;
        
        TreeNode * root=(TreeNode*)malloc(sizeof(TreeNode));
        
        int len=inorder.size();
        
        int pos=0;
        
        root->val=preorder[0];
        
        while(inorder[pos]!=root->val)pos++;
        
        root->left=build(inorder,preorder,0,pos-1,1,pos);
        root->right=build(inorder,preorder,pos+1,len-1,pos+1,len-1);
        
        return root;
    }
    TreeNode* build(vector<int>& inorder, vector<int>& preorder,int instart,int inend,int prstart,int prend)
    {


        if(instart>inend)return NULL;
        
        TreeNode * root=(TreeNode*)malloc(sizeof(TreeNode));
        
        root->val=preorder[prstart];
        
        int pos;
        
        for(pos=instart;pos<=inend;pos++)
            if(inorder[pos]==root->val)break;
        
        root->left=build(inorder,preorder,instart,pos-1,prstart+1,prend-(inend-pos));
        
        root->right=build(inorder,preorder,pos+1,inend,prend-(inend-pos)+1,prend);
        
        
        return root;
        
    }