演算法學習記錄四(C++)--->通過前序和中序序列重建二叉樹

Deft_MKJing宓珂璟發表於2017-08-03

描述

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

思路

  • 前序 DLR —> 根左右 第一個元素就是當前根節點
  • 中序 LDR —> 左根右 根元素分割了左子樹和右子樹


    1.我們首先從前序中找到第一個元素建立,就是根節點,然後拿這個根節點去中序序列中遍歷,找到後左側就是左子樹,右側就是右子樹
    2.然後分別通過數量,找到前序中的左右子樹,然後再進行前中左子樹和前中右子樹進行遞迴,直到葉節點不在有子樹
    3.先看下第一個例子,每次遞迴都建立出前序左右子樹vector和中序左右子樹vectot
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int inLenth = vin.size();
        // 遞迴結束的時候 vector為空
        if(inLenth == 0)
            return NULL;

        // 每次遞迴都建立新的向量 儲存DLR和LDR的L和R
        vector<int> left_pre,right_pre,left_in,right_in;
        //建立根節點,根節點肯定是前序遍歷的第一個數
        TreeNode* head=new TreeNode(pre[0]);
        //對於中序遍歷,根節點左邊的節點位於二叉樹的左邊,根節點右邊的節點位於二叉樹的右邊
        // 通過DLR的第一個數字,鎖定LDR中左右子樹的分割線
        int gen = 0;
        for(int i = 0;i<inLenth;i++){
            if(vin[i] == pre[0]){
                gen = i;
                break;
            }
        }
        // 獲取中前序中的左子樹
        for(int i = 0;i<gen;i++){
            left_pre.push_back(pre[i+1]);
            left_in.push_back(vin[i]);
        }
        // 這裡一定要從gen+1開始遍歷,避免出現陣列越界
        // 獲取中前序中的右子樹
        for(int i = gen+1;i<inLenth;i++){
            right_pre.push_back(pre[i]);
            right_in.push_back(vin[i]);
        }
        // 遞迴,每次把對應深度的跟節點返回,進行逆向迴歸
        //和shell排序的思想類似,取出前序和中序遍歷根節點左邊和右邊的子樹
        //遞迴,再對其進行上述所有步驟,即再區分子樹的左、右子子數,直到葉節點
        head->left = reConstructBinaryTree(left_pre,left_in);
        head->right = reConstructBinaryTree(right_pre,right_in);
        return head;

    }
};

更加精簡

  • 通過初始出來的前序序列和中序序列,只是改變其子樹的起始和終點進行遞迴遍歷
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        return Find(pre, 0, pre.size() - 1, vin, 0, vin.size() - 1);
    }
private:
    TreeNode* Find(const vector<int> &pre,int p_begin,int p_end,const vector<int> &vin,int v_begin,int v_end){
        // 遞迴結束 當開始的下標大於結束的下標,表示已經沒有子樹了
        if(p_begin > p_end || v_begin > v_end){
            return NULL;
        }

        // 前序 DLR-> 第一個數就是根節點
        TreeNode *root = new TreeNode(pre[p_begin]);

        // 遍歷 中序 LDR 分割出左子樹和右子樹
        int i = v_begin;
        for(;i<=v_end;i++){
            if(vin[i] == pre[p_begin]){
                break;
            }
        }
        // 獲取左子樹的數量和右子樹的數量
        int left_num = i-v_begin;
        int right_num = v_end - i;

        // 左右子樹分別進行遞迴
        root->left = left_num == 0 ? NULL : Find(pre,p_begin+1,p_begin+left_num,vin,v_begin,i);
        root->right = right_num == 0 ? NULL : Find(pre,p_begin+left_num+1,p_end,vin,i+1,v_end);
        return root;
    }

};

相關文章