1. 程式人生 > >資料結構基礎 層次遍歷和中序遍歷還原二叉樹

資料結構基礎 層次遍歷和中序遍歷還原二叉樹

【問題描述】

給出一個層次遍歷,和一箇中序遍歷的結果字串

層次  A B C D E F G
中序  D B A F E G C
其對應的二叉樹是:
       A
     /  /
     B  C
     /  /
     D  E
       / /
       F G

【演算法思想】

用LEV代表層次遍歷MID代表中序遍歷。然後我們還需要一個HLP陣列用來存放節點指標,其長度和層次/中序遍歷字串長度一樣,並且其對應於中序遍歷字串。


(0)起始狀態
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  0  0  0  0  0  0  0
L    0  0  0  0  0  0  0
R    0  0  0  0  0  0  0
在這裡,HLP存放的是節點指標,並且全都初始化為0,這裡的L/R代表了該節點是否有左/右孩子。在下面注意HLP和MID的對應關係。
(1) [A]BCDEFG
我們查詢A在MID中的位置,建立一個節點到對應位置,初建立的節 點沒有左右孩子。
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  0  0  A  0  0  0  0
L    0  0  0  0  0  0  0
R    0  0  0  0  0  0  0
(2) A[B]CDEFG
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  0  B  A  0  0  0  0
L    0  0  0  0  0  0  0
R    0  0  0  0  0  0  0
然後從B的左面開始找有沒有非0的指標,在這裡我們沒有找到
然後從B的右邊開始找有沒有非0的指標,找到了A
發現A沒有左孩子,令B為A的左孩子
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  0  B  A  0  0  0  0
L    0  0  X  0  0  0  0
R    0  0  0  0  0  0  0
(3) AB[C]DEFG
同上面步驟,發現C是A的右孩子
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  0  B  A  0  0  0  C
L    0  0  X  0  0  0  0
R    0  0  X  0  0  0  0
(4) ABC[D]EFG
同上,發現D是B的左孩子
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  D  B  A  0  0  0  C
L    0  X  X  0  0  0  0
R    0  0  X  0  0  0  0
(5) ABCD[E]FG
這裡有些不同,因為E向左找發現A的已經有了右孩子,所它必須向右找,結果發現E是C的左孩子
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  D  B  A  0  E  0  C
L    0  X  X  0  0  0  X
R    0  0  X  0  0  0  0
(6) ABCDE[F]G
同上,F做不了A的右孩子,只能做了E的左孩子
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  D  B  A  F  E  0  C
L    0  X  X  0  X  0  X
R    0  0  X  0  0  0  0
(7) ABCDEF[G]
G向左找,發現G可以做E的右孩子
同上,F做不了A的右孩子,只能做了E的左孩子
     0  1  2  3  4  5  6
MID  D  B  A  F  E  G  C
HLP  D  B  A  F  E  G  C
L    0  X  X  0  X  0  X
R    0  0  X  0  X  0  0
到此為止ABCDEFG的關係都已經建立好了,二叉樹還原完成。至於為什麼我們一定要先從左開始找,然後再向右找,也許是因為二叉樹的中序遍歷它本身就是從左到右的吧,具體沒有深究過。
這個演算法同樣適用於前序-中序,後序-中序的二叉樹還原。但是使用這個演算法你會發現需要申請與節點相同數量的臨時空間,如果這個二叉樹很大似乎會不太方便。而我的另外的前序-中序,後序-中序的二叉樹還原演算法所需要的臨時空間的最大數目是二叉樹的層次數。一個255個元素的滿二叉樹,層次為8,臨時空間至多需要8,顯然空間上較優。

【原始碼實現】

#include <iostream>
#include <stack>
#include <string>
#include <queue>
using namespace std;
struct TreeNode {
    char       data;
    TreeNode*   lChild;
    TreeNode*   rChild;
public:
    TreeNode(char c) : data(c), lChild(0), rChild(0) { }
};
//層次-中序二叉樹還原 
void Lev_Mid_Restore(string lev, string mid, TreeNode*& result) {
    const int size = lev.size();
    TreeNode* helper[size];
    for(int i = 0; i < size; i++)
        helper[i] = 0;
    /*            0  1  2  3  4  5  6  7  8
        helper    0  0  0  0  A  0  0  0  0
        lChild    0  0  0  0  0  0  0  0  0
        rChild    0  0  0  0  0  0  0  0  0
    */
    bool success = false;
    result = new TreeNode(lev[0]);
    int mi = mid.find(lev[0]);
    helper[mi] = result;
    
    for(int i = 1; i < lev.size(); i++) {
        success = false;
        mi = mid.find(lev[i]);
        helper[mi] = new TreeNode(lev[i]);  //把這個節點放到對應的陣列位置中 
        /*從當前節點X的左邊開始找,如果找到一個非0的節點Y,
          就判斷其右孩子是否為空,如果為空,則X是Y的右孩子,並且孩子配對成功,
          接下來就不需要再向右找了。 
          如果Y已經有右孩子了,則從節點X的右邊開始找 
        */ 
        for(int p = mi - 1; p >= 0; p--) {
            if(helper[p] != 0) {
                if(helper[p]->rChild == 0) {
                    helper[p]->rChild = helper[mi];
                    success = true;
                }
                break;
            }
        }
        if(success) {
            continue;
        }
        /*從當前節點X的右邊還是找,如果找到一個非0的節點Y
          判斷其左孩子是否為空,如果為空,則X是Y的左孩子
          如果Y已經有左孩子了,則說明這個中序/層次遍歷序列有問題
        */
        for(int p = mi + 1; p < size; p++) {
            if(helper[p] != 0) {
                if(helper[p]->lChild == 0) {
                    helper[p]->lChild = helper[mi];
                    success = true;
                }
                break;
            }
        }
        //因為既然到了這一步,還沒有配對成功,就說明給的中序/層次遍歷序列
        //有問題 
        if(!success) {
            cout << "error: " << lev[i] << endl;
            break;
        }
    }
}
int main() {
    void inorderTraversal(TreeNode* pTree);
    void levelorderTraversal(TreeNode* pTree);
    void Lev_Mid_Restore(string lev, string mid, TreeNode*& result);
    /*                 A
                    /      /
                   B        C
                 /   /    /   /
                D    E    F    G
               / /  / /  / /  / /
               H I  J K  M N  O P
    */
    string Levorder1 = "ABCDEFGHIJKMNOP";
    string Midorder1 = "HDIBJEKAMFNCOGP";
    
    string Levorder2 = "ABCDEFG";
    string Midorder2 = "BDAFEGC";
    TreeNode* res = 0;
    Lev_Mid_Restore(Levorder1, Midorder1, res);
    inorderTraversal(res);  
    levelorderTraversal(res);
    
    Lev_Mid_Restore(Levorder2, Midorder2, res);
    inorderTraversal(res);  
    levelorderTraversal(res);   
    
    cin.get();
}
//中序遍歷 
void inorderTraversal(TreeNode* pTree) {
    stack<TreeNode*> treeStack;
    do {
        while(pTree != 0) {
            treeStack.push(pTree);
            pTree = pTree->lChild;
        }
        if(!treeStack.empty()) {
            pTree = treeStack.top();
            treeStack.pop();
            cout << pTree->data;
            pTree = pTree->rChild;
        }
    }while(!treeStack.empty() || pTree != 0);
    cout << endl;
}
//層次遍歷 
void levelorderTraversal(TreeNode* pTree) {
    queue<TreeNode*> treeQueue;
    treeQueue.push(pTree);
    while(!treeQueue.empty()) {
        cout << treeQueue.front()->data;
        TreeNode* lChild = treeQueue.front()->lChild;
        TreeNode* rChild = treeQueue.front()->rChild;
        if(lChild != 0) {
            treeQueue.push(lChild);
        }
        if(rChild != 0) {
            treeQueue.push(rChild);
        }
        treeQueue.pop();
    }
    cout << endl;
}