1. 程式人生 > >二叉樹的遍歷及相關操作

二叉樹的遍歷及相關操作

一、先序遍歷(根左右),中序遍歷(左根右),後序遍歷(左右根)。只是根結點的遍歷順序發生變化,但左子樹的遍歷一定在右子樹遍歷之前進行。

//先序遍歷
void preorder(node* root){
    if(root==NULL)return;        //遞迴邊界。
    printf("%d",root->data);    //訪問根結點。
    preorder(root->lchild);
    preorder(root->rchild);
}

二、層序遍歷(BFS)

相當於對樹從根結點開始的BFS。

void layerorder(node* root){
    queue<node*>q;              //此處存的是node型變數的地址
    q.push(root);
    while(!q.empty()){
        node* now=q.front();    //取對首元素
        q.pop();                //出隊
        printf("%d",now->data);
        if(now->lchild!=NULL)
        q.push(now->lchild);    //左子樹入隊
        if(now->rchild!=NULL)
        q.push(now->rchild);
    }
}

若需要計算每個結點所處的層次。

struct node{
    int data;
    int layer;    //層號
    node* lchild;
    node* rchild;
}
//帶層次的層序遍歷
void layerorder(node* root){
    queue<node*>q;
    root->layer=1;
    q.push(root);
    while(!q.empty()){
        node* now=q.front();
        q.pop();
        printf("%d",now->data);
        if(now->lchild!=NULL){
            now->lchild->layer=now->layer+1;     //子樹層號為父親結點層號+1
            q.push(now->lchild);
        }
        if(now->rchild!=NULL){
            now->rchild->layer=now->layer+1;
            q.push(now->rchild);
        }
}

三、通過中序遍歷序列和其他三種遍歷序列之一,重建這棵二叉樹。

首先要知道三種遍歷的性質。

先序遍歷,序列中第一個數為根結點。

中序遍歷,確定根結點後,根結點左邊的序列為左子樹內容,右邊的序列為右子樹內容。

後序遍歷,序列中最後一個數為根結點。

這也論證了為什麼重建二叉樹時,一定要有中序遍歷序列,只有中序遍歷序列能區分左右子樹結點。如果知道了後序,前序和層序序列,只要不知道中序序列,就無法分辨左右子樹結點。無法唯一的重建該二叉樹。

1、通過前序和中序序列重建二叉樹。通過前序序列知道其根節點,在中序序列中找到該根節點所對應的唯一下標,由此區分左子樹區間和右子樹區間。在左子樹和右子樹中如此遞迴下去。直到先序序列長度小於等於0。例如遞迴過程中當前先序序列區間為【preL,preR】,中序序列區間為【inL,inR】。在中序序列中找到根節點下標為k。那麼左子樹結點個數 numLeft=k-inL。則先序序列中的左子樹區間為【preL+1,preL+numLeft】,右子樹區間為【preL+numLeft+1,preR】。中序序列的左子樹區間為【inL,k-1】,右子樹區間為【k+1,inR】。遞迴邊界為preL>preR。可以畫圖幫助理解。實現程式碼如下:


node* create(int preL,int preR,int inL,int inR){
    if(preL>preR){
        return NULL;
    }
    node* root=new node;
    root->data=pre[preL];    //  根節點。
    int k;  
    for(k=inL;k<inR;++k){
        if(pre[preL]==in[k])break;
    }
    int numLeft=k-inL;
    //左子樹的先序序列區間為[preL+1,preL+numLeft],左子樹的中序遍歷區間為[inL,k-1]
    root->lchild=create(preL+1,preL+numLeft,inL,k-1);
    //右子樹的先序序列區間為[preL+numLeft+1,preR],右子樹的中序遍歷區間為[k+1,inR]
    root->rchild=create(preL+numLeft+1,preR,k+1,inR);
    return root;

2、同理若通過後序序列和中序序列,重建唯一二叉樹。假設後序序列區間為【postL,postR】,中序序列區間為【inL,inR】。左子樹的結點個數為numLeft=k-inL。則後序序列中的左子樹區間為【postL,postL+numLeft-1】,右子樹區間為【postL+numLeft,postR-1】,中序序列的左子樹區間為【inL,k-1】,右子樹區間為【k+1,inR】。遞迴邊界為postL>postR。

3、通過層序遍歷序列和中序遍歷序列,重建唯一二叉樹。層序遍歷序列第一個結點就是根結點,可通過根結點和中序遍歷序列確定左右子樹結點。但在層序序列中,左右子樹結點是相互交叉在一起的。所以可以開兩個陣列分別儲存左右子樹的結點遍歷序列,相互區分

node* create(vector<int>layer,inL,inR){
    if(layer.size()==0)return NULL;    //結點已經全部遍歷完成。
    node* root=newNode(layer[0]);    //newNode()是建立新結點函式。
    int k;
    for(k=inL;k<=inR;++k){
        if(layer[0]==in[k])break;
    }
    vector<int>leftlayer;     //左子樹層序遍歷序列
    vector<int>rightLayer;    //右子樹層序遍歷序列
    for(int i=1;i<layer.size();++i){    //在整個層序遍歷序列中
        bool isLeft=false;
        for(int j=inL,j<k;++j){
            if(layer[i]==in[j]){
                isLeft=true;    //找到了左子樹層序序列結點。
                break;
             }
         }
        if(isLeft)leftlayer.push_back(layer[i]);
        else rightlayer.push_back(layer[i]);
    }
    root->lchild=create(leftlayer,inL,k-1);
    root->rchild=create(rightlayer,k+1,inR);
return root;
}