二叉樹的遍歷

二叉樹的遍歷

二叉樹是一種常用並且重要的數據結構,其是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”和“右子樹”。二叉樹中一個比較常用的操作是遍歷二叉樹。

所謂遍歷(Traversal)是指沿着某條搜索路線,依次對樹中每個結點均做一次且僅做一次訪問。訪問結點所做的操作依賴於具體的應用問 題。
遍歷是二叉樹上最重要的運算之一,是二叉樹上進行其它運算之基礎。
———-[百度百科]

本文中使用的二叉樹的節點結構如下:

struct TreeNode {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  };

二叉樹的遍歷根據訪問節點的順序不同分爲三種:前序遍歷(Preorder),中序遍歷(Inorder),後序遍歷(PostOrder)。代碼可以分成遞歸和非遞歸兩種實現方式。

前序遍歷

前序遍歷是對於一個節點,我們先訪問該節點,再訪問該節點的左子樹,最後訪問該節點的右子樹。
遞歸的實現方式如下:

void preorder(TreeNode *root, vector<int> &result)
    {
        if(root)
        {
            result.push_back(root->val);
            if(root->left) 
                preorder(root->left,result);//遞歸左子樹
            if(root->right)
                preorder(root->right,result);//遞歸右子樹
        }
    }

    vector<int> preorderTraversal(TreeNode *root) {
        vector<int> result;
        preorder(root,result);
        return result;
    }

非遞歸實現方式如下:

vector<int> preorderTraversal(TreeNode *root) {
        vector<int> result; 
        if(!root) return result;

        stack<TreeNode*> s;
        s.push(root);

        while(!s.empty())
        {
            TreeNode* n = s.top(); 
            result.push_back(n->val);
            s.pop();
            //根據棧後進先出的特點先存入右子樹,再存入左子樹,確保                                了每次都先訪問節點的左子樹,然後是右子樹
            if(n->right) s.push(n->right);
            if(n->left) s.push(n->left);

        }

        return result;
    }

非遞歸的核心是利用棧,先將根節點放入棧中,然後不斷對棧中元素訪問,每出棧一個元素,就可看成訪問了該元素,然後根據棧後進先出的特點先存入節點右子樹,再存入左子樹,確保了每次都先訪問節點的左子樹,然後是右子樹。

中序遍歷

中序遍歷是對於一個節點,我們先訪問該節點的左子樹,再訪問該節點,最後訪問該節點的右子樹。
遞歸的實現方式

void inorder(TreeNode *root,vector<int> &ret)
    {
        if(root)
        {
            inorder(root->left,ret);
            ret.push_back(root->val);
            inorder(root->right,ret);
        }
    }
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> ret;
        inorder(root,ret);
        return ret;
    }

非遞歸的實現方式

vector<int> inorderTraversal(TreeNode *root) {
        vector<int> ret;
        stack<TreeNode *> s;
        TreeNode *p = root;      
        do
        {
            while (p != NULL)
            {
                s.push(p);
                p = p->left;
            }

            if (!s.empty())
            {
                p = s.top();
                s.pop();

                ret.push_back(p->val);

                p = p->right;
            }
        } while (!s.empty() || p != NULL);

        return ret; 
    }

非遞歸的實現也是利用了棧,當遇到一個節點時,先遍歷到其左子樹的最左邊,途中不斷地入棧,則每當出棧一個元素時,其左子樹已經完全被遍歷,然後訪問該節點,之後指向該節點的右子樹。

後序遍歷

後序遍歷是對於一個節點,我們先訪問該節點的左子樹,再訪問該節點的右子樹,最後訪問該節點。
遞歸方式:

void postorder(TreeNode *root,vector<int> &result)
    {
        if(root)
        {
            postorder(root->left,result);
            postorder(root->right,result);
            result.push_back(root->val);
        }
    }

    vector<int> postorderTraversal(TreeNode *root) {
        vector<int> result;
        postorder(root,result);
        return result;
    }

非遞歸方式:

vector<int> postorderTraversal(TreeNode *root) {
        vector<int> result;
        if(!root) return result;

        stack<TreeNode*> stack;
        stack.push(root);

        while(!stack.empty())
        {
            TreeNode* node = stack.top();
            result.push_back(node->val);
            stack.pop();

            if(node->left) stack.push(node->left);
            if(node->right) stack.push(node->right);
        }
        reverse(result.begin(), result.end());

        return result;
    }

後序遍歷要是直接用非遞歸的方式實現會有點麻煩,我們可以按照先節點,再右子樹,再左子樹的順便遍歷,而這個順序的實現方式我們可以參照先序遍歷,只不過是左右子樹的入棧順序變一下,之後再將遍歷的節點逆序即可。

層次遍歷

層次遍歷是一層層遍歷的樹,如下圖:
這裏寫圖片描述
該樹的層次遍歷結果是:3,9,20,15,7.
遞歸實現方式,也可以看成是DFS(深度優先搜索):

void solve(int dep,TreeNode *root,vector<vector<int>> &result)
    {
        if(!root)
            return;
        if(dep>=result.size())
        {
            vector<int> res;
            res.push_back(root->val);
            result.push_back(res);
        }else{
            result[dep].push_back(root->val);
        }

        solve(dep+1,root->left,result);
        solve(dep+1,root->right,result);
    }

    vector<vector<int> > levelOrder(TreeNode *root) {
        vector<vector<int>> result;  
        solve(0,root,result);    
        return result;  
    }

非遞歸實現方式,可看成是BFS(廣度優先搜素)

 vector<vector<int> > levelOrder(TreeNode *root) {
        vector<vector<int>> result;  
        if(root == NULL) return result;  

        queue<TreeNode*> q;  
        queue<int> queue_level;  
        q.push(root);   
        queue_level.push(0);  

        vector<int> elem;  
        result.push_back(elem);  

        while(q.size() > 0){  
            TreeNode* x = q.front();  
            q.pop();  
            int l = queue_level.front();  
            queue_level.pop();  

            if(l > (result.size()-1) ){  //需要新增一個層級
                vector<int> elem;  
                result.push_back(elem);  
            }  
            result[l].push_back(x->val);  
             //將左右節點加入到隊列中,層級需要加1 
            if(x->left != NULL){  
                q.push(x->left);  
                queue_level.push(l+1);  
            }  

            if(x->right != NULL){  
                q.push(x->right);  
                queue_level.push(l+1);  
            }  

        }  

        return result;  
    }

廣度優先搜素需要維護兩個隊列,一個用來保存節點,一個來保存節點的層級,兩者在兩個隊列裏一一對應,根節點是0層。每次取兩個隊列的頭元素,判斷層級和結果集result的大小,做出對應的操作(和遞歸一樣的操作)。之後將節點的左右節點加入到隊列中,直到遍歷所有節點。

發佈了42 篇原創文章 · 獲贊 32 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章