二叉樹遍歷 | 遞歸 & 非遞歸

中序遍歷

一直遍歷左子樹到空,然後轉移到右子樹

void inorder(TreeNode *root,vector<int> &path){
    stack<TreeNode*> s;
    TreeNode* p=root;
    while(p||!s.empty()){  //節點不爲空時,或者棧不爲空的情況下繼續執行循環體
        //一直遍歷左子樹直到底
        while(p!=NULL){
            s.push(p);
            p=p->left;
        }
        //p爲空,說明已經到了左子樹的最下面,這時需要出棧
        if(!s.empty()){
            p=s.top();
            path.push_back(p->val);
            s.pop();
            //進入右子樹,開始新一輪的左子樹遍歷(遞歸的自我實現)
            p=p->right;
        }
    }
}

先序遍歷

先序遍歷和中序遍歷差不多,也是需要藉助根節點訪問右子樹,只不過和中序遍歷的區別在於在根節點被壓棧時就輸出它的值,而不是在出棧的時候再訪問

void preOrder(TreeNode* root,vector<int> &path){
    if(root==NULL) return;
    stack<TreeNode*> s;
    TreeNode* p=root;
    while(!s.empty()||p!=NULL){
        while(p){
            path.push_back(p->val);
            s.push(p);
            p=p->left;
        }
        if(!s.empty()){
            TreeNode* temp=s.top();
            s.pop();
            p=temp->right;
        }
    }
}

後序遍歷

需要判斷上次訪問的是位於左子樹還是右子樹。若位於左子樹,則要跳過根節點,先進入右子樹,再訪問根節點;若是位於右子樹,則直接訪問根節點。

需要保存當前訪問節點和上次訪問節點

後序遍歷訪問的一直都是從棧中彈出的元素

  1. 使用棧來實現,並設置兩個指針,分別指向當前訪問的節點和上次訪問的節點
  2. 初始化時將當前訪問的節點指向最左下的節點,上次訪問的節點爲NULL
  3. 取棧頂元素爲當前訪問節點,如果此節點沒有右孩子或者是右孩子爲上次訪問節點,那麼輸出此節點信息,並將此節點賦值給上次訪問節點,繼續從棧中取元素
  4. 如果右孩子不是上面說的那種情況的話,將當前訪問節點(也就是原來的棧頂元素)再次壓入棧中,然後訪問右子樹,將右子樹的左節點左節點持續壓入棧中,繼續循環第3步
void postOrder(TreeNode* root,vector<int> &path){
    if(root==NULL) return;
    stack<TreeNode*> s;
    //當前訪問節點,上次訪問節點
    TreeNode *pCur,*pLastVisit;
    pCur=root;
    pListVisit=NULL;
    //從左子樹的最下邊開始訪問
    while(pCur){
        s.push(pCur);
        pCur=pCur->left;
    }
    while(!s.empty()){
        //走到這裏時,已經遍歷到二叉樹的最低端
        pCur=s.top();
        s.pop();
        //如果沒有右子樹或者右子樹已經被訪問,那麼訪問這個根節點
        if(pCur->right==NULL||pCur->right==pLastVisit){
            path.push_back(pCur->val);
            //修改爲最近被訪問的節點
            //只有輸出結果的時候纔會修改上次訪問節點
            pLastVisit=pCur;
        }
        else{
            //將根節點再次入棧
            s.push(pCur);
            //訪問右子樹,進入右子樹
            pCur=pCur->right;
            while(pCur){
                s.push(pCur);
                pCur=pCur->left;
            }
        }
    }
}

層序遍歷

層序遍歷用非遞歸的方法更容易實現些,使用隊列保存節點,然後逐層遍歷即可

用遞歸實現的方法並不常用,首先要知道樹的深度,逐層輸出(這裏的遞歸的作用似乎就只是到達了對應的層)

void level(TreeNode* root,int i,vector<int>& path){
    if(root==NULL||i==0) return;
    if(i==1){
        path.push_back(root->val);
    }
    level(root->left,i-1,path);
    level(root->right,i-1,path);
}

int getDepth(TreeNode *root,int depth){
    if(root==NULL)
        return depth;
    else{
        int l=getDepth(root->left,depth+1);
        int r=getDepth(root->right,depth+1);
        return max(l,r);
    }
}

void levelOrder(TreeNode* root,vector<int>& path){
    if(root==NULL) return;
    int dep=getDepth(root);
    for(int i=1;i<=dep;i++){
        level(root,i,path);
    }
}

 

測試代碼

int main(){
    TreeNode* one=new TreeNode();
    one->val=1;
    TreeNode* two=new TreeNode();
    two->val=2;
    TreeNode* three=new TreeNode();
    three->val=3;
    TreeNode* four=new TreeNode();
    four->val=4;
    one->right=two;
    one->left=three;
    three->left=four;
    
    vector<int> path;
//    levelOrder(one,path);
//    inorder(one,path);
//    preOrder(one,path);
    postOrder(one,path);
    for(int i=0;i<path.size();i++){
        cout<<path[i]<<" ";
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章