1. 程式人生 > >【劍指offor】4、重建二叉樹

【劍指offor】4、重建二叉樹

牛客網題目連結:重建二叉樹

題目描述:
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。

1、題目分析

題目給出前序遍歷和中序遍歷,讓重建二叉樹。由前序遍歷特性知前序遍歷的第一個節點,肯定是根節點。那麼找到了根節點,接下來找到左子樹與右子樹即可。由中序遍歷的特性知中序遍歷中,根節點位於中間某一個位置,它的左邊是左子樹節點,右邊是右子樹的節點。從而我們就找到了左子樹的所有節點與右子樹的所有節點以及根節點。然後我們讓根節點左指標指向左子樹的根節點,根節點右子樹指標指向右子樹的根節點。那麼接下來的問題就是要求左子樹的根節點與右子樹的根節點,然後再讓左子樹的根節點指向它的左子樹與右子樹,右子樹的根節點指向它的左子樹與右子樹…很明顯,這是一個遞迴過程。

下面我們看以下圖示,來理解找根節點,左子樹與右子樹的過程。

假設某一棵樹如下:

在這裡插入圖片描述

它的前序遍歷位:[1,2,4,7,3,5,6,8] 後序遍歷:[4,7,2,1,5,3,8,6]

那麼第一次找根節點與左右子樹,如下圖:

在這裡插入圖片描述

此時,可以得出如下我們要求的二叉樹是如下樣式:

在這裡插入圖片描述

以上我們只知道左子樹和右子樹包含哪些節點,並不知道左子樹和右子樹長什麼樣。

接下來我們再遞迴的求解左子樹序列與右子樹序列。,找到左子樹的根節點與右子樹的根節點。

遞迴求解左子樹:

在這裡插入圖片描述

此時,可以得出如下二叉樹的形式:

在這裡插入圖片描述

同理,此時對於左子樹的根節點2,再對其遞迴求解它的左子樹與右子樹的二叉樹,其中右子樹位空不用求解直接指向null,左子樹還有節點,那麼利用左子樹的節點的前序序列與中序序列,再次遞迴求解即可。

最終得出如下圖所示的左子樹二叉樹:

在這裡插入圖片描述

遞迴求解右子樹:

左子樹遞迴求解完後,就遞迴求解右子樹:

在這裡插入圖片描述

此時可以得到如下二叉樹的形式:
在這裡插入圖片描述

同理,測試再對於右子樹的節點3,再對其進行遞迴求它的左子樹與右子樹,最終得到右子樹的二叉樹為:

在這裡插入圖片描述

所以,最終的二叉樹就是如下圖:

在這裡插入圖片描述

2、程式碼

2.1、java程式碼

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length-1==0 || in.length-
1==0 || pre.length != in.length)return null; return reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1); } public TreeNode reConstructBinaryTree(int[] pre,int preStart,int preEnd,int[] in,int inStart,int inEnd){ int rootVal=pre[preStart]; TreeNode root=new TreeNode(rootVal); root.left=root.right=null; if(preStart==preEnd || inStart==inEnd)return root; int inIndex=inStart; while(in[inIndex]!=rootVal && inIndex<=inEnd)inIndex++; int lnum=inIndex-inStart; int rnum=inEnd-inIndex; if(lnum>0){ root.left=reConstructBinaryTree(pre,preStart+1,preStart+lnum,in,inStart,inIndex-1); } if(rnum>0){ root.right=reConstructBinaryTree(pre,preStart+lnum+1,preEnd,in,inIndex+1,inEnd); } return root; } }

註釋:程式碼中inIndex代表中序遍歷中根節點的位置,lnum代表左子樹節點的個數,rnum代表右子樹節點的個數。

2.2 C++程式碼

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if(pre.size()==0 || vin.size()==0 || pre.size()!=vin.size())return nullptr;
        return reConstructBinaryTree(pre,0,pre.size()-1,vin,0,vin.size()-1);
    }
    TreeNode* reConstructBinaryTree(vector<int> pre, int preStart,int preEnd,
                                    vector<int> vin, int vinStart,int vinEnd){
        int rootVal=pre[preStart];
        TreeNode* root = new TreeNode(rootVal);
        root->left=nullptr;
        root->right=nullptr;
        if(preStart == preEnd || vinStart==vinEnd)return root;
        int vIndex = vinStart;
        while(rootVal != vin[vIndex] && vIndex <= vinEnd)vIndex++;
        int lnum = vIndex-vinStart;
        int rnum = vinEnd-vIndex;
        int leftIndex = preStart+lnum;
        if(lnum>0){
            root->left = reConstructBinaryTree(pre,preStart+1,leftIndex,vin,vinStart,vIndex-1);
        }
        if(rnum>0){
            root->right = reConstructBinaryTree(pre,leftIndex+1,preEnd,vin,vIndex+1,vinEnd);
        }
        return root;
    }
};

註釋:上面vIndex代表中序遍歷中根節點的位置,lnum代表左子樹的節點數目,rnum代表右子樹的節點的數目。

3、總結

以上程式碼並不是最簡潔的程式碼,但是目的是為了突出這種演算法的實現過程。希望每個人都能很容易就理解這種遞迴的過程。

學習探討加:
qq:1126137994
微信:liu1126137994