二叉树的遍历总结

二叉树的遍历总结

二叉树是一种非常常见的数据结构,对图的遍历我们一般有深度优先遍历广度优先遍历两种,而树是一种特殊的图,即无环图。所以我们对树的遍历一般也采用以上两种方法,特殊的,对于二叉树,我们习惯地将深度优先遍历分为中序遍历前序遍历后序遍历三种,而层次遍历则为广度优先遍历。

中序遍历

中序遍历过程的顺序是左 -> 根 -> 右

递归算法

递归算法比较简单,就根据中序遍历的过程,先遍历左子树,再遍历当前根的值,然后遍历右子树。当遍历到根结点时,将该点加入遍历数组即可。递归函数最重要的是中止条件,当遍历当前结点为空时,即可返回。

调用递归函数时可以看作它已经帮你完成了你所想要完成的所有事,在递归函数结束后,它就已经将答案封装好交到了你的手上。

/**
 * 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<int> res;
    vector<int> inorderTraversal(TreeNode* root) {
        dfs(root);
        return res;
    }

    void dfs(TreeNode* root)
    {
        if (!root) return;
        dfs(root -> left);
        res.push_back(root -> val);
        dfs(root -> right);
    }
};

迭代算法

递归函数实现的过程其实就是系统帮我们调用栈的过程,所以如果换为迭代算法来写,我们只需要自己模拟实现一个栈。

递归的实现也可以反映成一棵树,就是所谓的递归树,当我们调用递归函数的时候,其实有两个过程,一个是向下的的过程,然后再是向上的的过程。递归函数很好地封装了这一点,然而当我们用迭代算法时,需要自己去模拟的过程。

遍历是,我们需要将所有左子树链上的所有点放入栈中,该过程即为,然后取出栈顶,加入遍历数组,之后再放入右子树,同时注意这里的右子树指的是整个右子树的左子树链。直到遍历完所有结点。

/**
 * 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<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;

        auto p = root;

        while (p || !st.empty())
        {
            while (p) //将二叉树左链条全部入栈
            {
                st.push(p);
                p = p -> left;
            }

            p = st.top(); //左链条全部进栈或栈不为空时,弹出并遍历
            st.pop();
            res.push_back(p -> val);
            p = p -> right;
        }

        return res;
    }
};

二叉树中序遍历.jpg

2. 前序遍历

前序遍历顺序为根 -> 左 -> 右,所以运用递归算法时,先将该点放入遍历数组,再递归遍历左子树和右子树

递归算法

/**
 * 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<int> res;
    vector<int> preorderTraversal(TreeNode* root) {
        dfs(root);
        return res;
    }

    void dfs(TreeNode* root)
    {
        if (!root) return;
        res.push_back(root -> val);
        dfs(root -> left);
        dfs(root -> right);
    }
};

迭代算法

当前遍历结点即为,所以在迭代算法中,先将该点放入遍历数组,然后将左子树链到最低层,然后出栈,放入右子树。

/**
 * 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<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;
        auto p = root;

        while (p || !st.empty())
        {
            while (p)
            {
                res.push_back(p -> val);
                st.push(p);
                p = p -> left;
            }

            p = st.top();
            st.pop();
            p = p-> right;
        }

        return res;
    }
};

3. 后序遍历

后序遍历的顺序为左 -> 右 -> 根

根据观察,后序遍历倒过来的顺序为根 -> 右 -> 左

而我们已经知道如何进行先序遍历根 -> 左 -> 右

在递归算法中比较好理解,只需要调整遍历顺序。但是在迭代算法中,我们首先需要先模拟先序遍历的过程,同时将先序遍历中左右子树遍历过程交换,最后还需将遍历数组翻转才可以得到后序遍历。

递归算法

/**
 * 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<int> res;
    vector<int> postorderTraversal(TreeNode* root) {
        dfs(root);
        return res;
    }

    void dfs(TreeNode* root)
    {
        if (!root) return;
        dfs(root -> left);
        dfs(root -> right);
        res.push_back(root -> val);
    }
};

迭代算法

/**
 * 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<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;

        auto p = root;
        while (p || !st.empty())
        {
            while (p)
            {
                res.push_back(p -> val);
                st.push(p);
                p = p -> right;
            }

            p = st.top();
            st.pop();
            p = p -> left;
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

4. 层次遍历

算法思路

层序遍历,顾名思义需要对二叉树进行一层一层的遍历。因此可以采用宽度优先搜索BFS的思路,根据BFS的性质,每进行一次扩展,会将下一层的点全部放入队列中,所以每次在对队列进行取出操作时,当前队列中的所有元素均当前层元素。因为我们需要一边取出元素,一边放入元素。所以在每一次更迭前,先记录当前的队列的大小,即为当前层的元素,然后取出队头的该几个元素,放入当前层遍历数组level中,同时对左右儿子进行扩展。每遍历完一层,最后将level数组插入最终遍历数组中。

C ++代码

/**
 * 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) {
        vector<vector<int>> res;
        if (!root) return res;

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

        while (q.size())
        {
            int n = q.size();
            vector<int> level;

            for (int i = 0; i < n; i ++)
            {
                auto t = q.front();
                q.pop();
                level.push_back(t -> val);
                if (t -> left) q.push(t -> left);
                if (t -> right) q.push(t -> right);
            }
            
            res.push_back(level);
        }

        return res;
    }
};

相对于迭代算法来说,递归算法代码更简洁,在使用递归函数的时候我们不需要考虑计算机对栈的调用细节。在迭代算法里,需要用while循环来模拟实现递归函数的过程。

本文给出的二叉树遍历代码简洁易读,当理解完二叉树的遍历之后,大家可以试着写一写多叉树的遍历。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值