遍歷二叉樹的迭代和遞歸方法

二叉樹的問題,必定要明白到底應該深度優先(前中後序)仍是廣度優先(層序遍歷)node

最基本的遍歷方式:深度優先和廣度優先數組

  深度優先:前、中、後序(遞歸法和迭代法都可)框架

  廣度優先:層次遍歷(迭代法)post

其實就是遞歸的一種實現結構,也就是說前中後序遍歷的邏輯其實都是能夠藉助棧使用非遞歸的方式來實現的;spa

廣度優先遍歷(層序遍歷)的實現通常使用隊列來實現,這也是隊列先進先出的特色所決定的,由於須要先進先出的結構,才能一層一層的來遍歷二叉樹指針

二叉樹節點的定義框架:code

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

二叉樹的遞歸遍歷框架:blog

/*二叉樹的遍歷框架*/
void traverse(TreeNode root)
{
    //前序遍歷:先訪問根節點,再前序訪問左子樹,再訪問右子樹
    traverse(root->left);
    //中序遍歷:先中序訪問左子樹,再訪問根節點,再訪問右子樹
    traverse(root->right);
    //後續遍歷:前後續訪問左子樹,再訪問右子樹,再訪問根節點
}

1、二叉樹的前序遍歷:迭代和遞歸遞歸

class Solution {
public:
    //vector<int> result;//遞歸的話定義在這裏
    vector<int> preorderTraversal(TreeNode* root) {
        //遞歸方式
        /*
        if(root == nullptr)
            return {};
        result.push_back(root->val);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
        return result;
        */
        //固然可使用迭代解法,由於遞歸自己就是用棧來實現的,能夠經過棧來迭代操做
        //可是要注意棧的特性是後入先出,前序的話,就是先放入根節點賦值操做彈出,再放入右節點、左節點,再彈出,這樣左節點就會先出,先賦值操做,就是前序了
        stack<TreeNode*> sta;
        vector<int> result;
        sta.push(root);
        while(!sta.empty()) {
            int size = sta.size();
            for(int i=0; i<size; i++) {
                TreeNode* node = sta.top();
                sta.pop();
                result.push_back(node->val);
                if(node->right)
                    sta.push(node->right);
                if(node->left)
                    sta.push(node->left);
            }
        }
        return result;
    }
};

2、二叉樹的後序遍歷:迭代和遞歸隊列

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    //vector<int> result;//遞歸解法定義在這裏
    vector<int> postorderTraversal(TreeNode* root) {
        /*
        if(root == nullptr)
            return {};
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        result.push_back(root->val);
        return result;
        */
        //本題還能夠採用迭代解法,由於遞歸就是用棧來實現的
        //考慮實現的過程
        //後序遍歷是左右中的順序,可是咱們在迭代的時候確定會先訪問根節點,也就是中間的節點,因此考慮先訪問和處理中間節點,再處理右節點,再處理左邊節點,最後將結果翻轉就好了
        stack<TreeNode*> sta;
        vector<int> result;
        sta.push(root);
        while(!sta.empty()) {
            int size = sta.size();
            for(int i=0; i<size; i++) {
                TreeNode* node = sta.top();
                sta.pop();
                result.push_back(node->val);
                if(node->left)
                    sta.push(node->left);
                if(node->right)
                    sta.push(node->right);
            }
        }
        reverse(result.begin(), result.end());
        return result;
    }
};

3、二叉樹的中序遍歷:迭代和遞歸

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    //vector<int>result;//遞歸寫法這裏定義
    vector<int> inorderTraversal(TreeNode* root) {
        /*遞歸解法
        if(root == nullptr)
            return {};
        inorderTraversal(root->left);
        result.push_back(root->val);
        inorderTraversal(root->right);
        return result;
        */
        //還能採用迭代解法,用棧來解決,由於遞歸自己就是用棧來實現的,所以是徹底行得通的
        //中序的順序是左中右,那出棧的時候,處理的順序確定是右中左
        //搞清楚訪問和處理的概念
        //訪問:將節點入棧
        //處理:將節點的值放入結果集
        //中序的訪問和處理的順序是不同的,因此要藉助指針進行訪問,也就是將節點放入棧中,用棧來作處理,也就是放入結果集
        vector<int> result;
        stack<TreeNode*> sta;
        TreeNode* cur = root;
        while(cur != nullptr || !sta.empty()) {
            if(cur != nullptr) {//指針用來訪問節點,訪問到左邊最底層的時候,指針和要開始處理的位置就同樣了
                sta.push(cur);//將訪問的節點放進棧
                cur = cur->left;//最左的子節點最後放進去,因此會先出棧    左
            }
            else {
                cur = sta.top();
                sta.pop();
                result.push_back(cur->val);                             //
                cur = cur->right;                                       //
            }
        }
    }
};

4、二叉樹的層序遍歷:迭代和遞歸

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;//建立一個隊列,層序遍歷樹的須要用隊列來實現,隊列中是二叉樹的節點
        if(root != nullptr)
            que.push(root);//若是頭結點不爲空的話,先將頭結點放到隊列中,由於頭結點也就是第一行,只有這一個元素,因此直接放進去
        vector<vector<int>> result;//定義返回值,返回的是一個二維數組
        while(!que.empty()) {
            int size = que.size();//同一行可能不止一個元素,要循環都進行遍歷,又由於下面要進行pop操做,que.size()是一個變化的值,因此這裏存儲數量
            vector<int> vec;//用於臨時存儲每一行的節點值,最後統一存入返回的二維數組中
            for(int i=0; i<size; i++) {
                TreeNode* node = que.front();
                que.pop();//
                vec.push_back(node->val);
                if(node->left)
                    que.push(node->left);//將這個節點的左右子節點放入隊列中
                if(node->right)
                    que.push(node->right);
            }
            result.push_back(vec);
        }
        return result;
    }
};

掌握了層序遍歷的模板,別的層序遍歷相關的題只要稍微改動幾行代碼就能夠解決了。

遇到二叉樹的題目,必定要想想究竟是用深度優先遍歷仍是廣度優先遍歷,到底使用迭代法仍是用遞歸法。

相關文章
相關標籤/搜索