樹的遞歸與非遞歸遍歷方法


關於二叉樹的遍歷在面試時是企業經常容易考到的題目,這里做一下總結。

各類二叉樹遍歷的概念

二叉樹有前序遍歷,中序遍歷和后序遍歷三種。關於這三種遍歷,網上資料有很多,在此就不做詳細介紹了。主要需要記住順序:

  • 前序遍歷 - 根->左->右
  • 中序遍歷 - 左->根->右
  • 后序遍歷 - 左->右->根

遞歸時僅需要按照上述順序就可以了。

前序遍歷遞歸:

void preOrder(TreeNode* root) {
    if (!root) {
        return;
    }
    cout << root->val;
    preOrder(root->left);
    preOrder(root->right);
}

中序遍歷遞歸:

void inOrder(TreeNode* root) {
    if (!root) {
        return;
    }
    inOrder(root->left);
    cout << root->val;
    inOrder(root->right);
}

后序遍歷遞歸:

void postOrder(TreeNode* root) {
    if (!root) {
        return;
    }
    postOrder(root->left);
    postOrder(root->right);
    cout << root->val;
}

非遞歸版本:

可見遞歸版本實現起來非常簡單,面試的時候,往往面試官會強制你寫出非遞歸的版本,網上關於非遞歸版本的介紹也有很多,這里我分享一個自己認為是比較好記的版本。

顯然,我們需要用一個stack來模擬遞歸時的函數調用。對於三種遍歷,我們都使用push當前節點->push左子樹->pop左子樹->push右子樹->pop右子樹的方式。但是cout時機會有所不同。

對於前序遍歷來說,每次訪問到一個節點就cout;

對於中序遍歷來說,每次將右子節點進棧時,把當前節點cout;

對於后序遍歷來說,每次pop的時候cout。

另外我們還需要一個last_pop指針來存放上一個pop出去的節點。

如果當前節點的左右節點都不是上一個pop的節點,那么我們將左子節點入棧;

如果當前節點的左節點是上一個pop的節點,但右節點不是,那么就把右子節點入棧;

否則的話,就需要讓當前節點出棧。

大致思路就是這樣,俗話說Talk is cheap, let's coding. 直接上代碼,注意三種遍歷的代碼總體結構都是完全一樣的,只是cout的時機有所不同。

前序遍歷非遞歸:

void preorder_traversal_iteratively(TreeNode* root)
{
    if (root == 0)
        return;
    stack<TreeNode*> s;
    s.push(root);
    cout << root->val << ' '; // visit root
    TreeNode* last_pop = root;
    while (!s.empty())
    {
        TreeNode* top = s.top();
        if (top->left != 0 && top->left != last_pop && top->right != last_pop) // push_left
        {
            s.push(top->left);
            cout << top->left->val << ' '; // visit top->left
        }
        else if (top->right != 0 && top->right != last_pop && (top->left == 0 || top->left == last_pop)) // push_right
        {
            s.push(top->right);
            cout << top->right->val << ' '; // visit top->right
        }
        else // pop
        {
            s.pop();
            last_pop = top;
        }
    }
}

中序遍歷非遞歸:

void inorder_traversal_iteratively(TreeNode* root)
{
    if (root == 0)
        return;
    stack<TreeNode*> s;
    s.push(root);
    TreeNode* last_pop = root;
    while (!s.empty())
    {
        TreeNode* top = s.top();
        if (top->left != 0 && top->left != last_pop && top->right != last_pop) // push_left
        {
            s.push(top->left);
        }
        else if (top->right != 0 && top->right != last_pop && (top->left == 0 || top->left == last_pop)) // push_right
        {
            s.push(top->right);
            cout << top->val << ' '; // visit top
        }
        else // pop
        {
            s.pop();
            last_pop = top;
            if (top->right == 0)
                cout << top->val << ' '; // visit top
        }
    }
}

后序遍歷非遞歸:

void postorder_traversal_iteratively(TreeNode* root)
{
    if (root == 0)
        return;
    stack<TreeNode*> s;
    s.push(root);   
    TreeNode* last_pop = root;
    while (!s.empty())
    {       
        TreeNode* top = s.top();
        if (top->left != 0 && top->left != last_pop && top->right != last_pop) // push_left
        {
            s.push(top->left);
        }
        else if (top->right != 0 && top->right != last_pop && (top->left == 0 || top->left == last_pop)) // push_right
        {
            s.push(top->right);
        }
        else // pop
        {
            s.pop();
            last_pop = top;
            cout << top->val << ' '; // visit top
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM