【数据结构】遍历二叉树

前言

二叉树有前中后序和层序四种常用的遍历方式,今天我们来学习一下如何用这四种方法遍历二叉树。
前序:根、左、右
中序:左、右、根
后序:左、右、根
层序:第一层、第二层…

递归

递归是一种将复杂问题不断细分成小问题的方法。
既然是划分大问题为小问题,就要考虑明白什么时候是最小的问题,也就是我们常说的终止条件,就拿前序遍历举例子:
前序遍历要求我们按根、左、右的顺序去访问树,那我们访问完根节点,就可以将问题转换为子问题去访问他的左子树了。
当访问完根节点,再去访问根的左节点,当这个根的左节点访问完毕,最后去访问根的右节点,当遇到空的时候就终止。
在这里插入图片描述

  • 前序
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        Traversal(root,result);
        return result;
    }

    void Traversal(TreeNode* cur,vector<int>& result){
        if(cur == NULL){
            return; 
        }
        result.push_back(cur->val);      
        Traversal(cur->left, result);
        Traversal(cur->right, result);
    }
};
  • 中序
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        Traversal(root,result);
        return result;
    }

    void Traversal(TreeNode* cur,vector<int>& result){
        if(cur == NULL){
            return; 
        }     
        Traversal(cur->left, result);
        result.push_back(cur->val);
        Traversal(cur->right, result);
        
    }
};
  • 后序
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        Traversal(root,result);
        return result;
    }

    void Traversal(TreeNode* cur,vector<int>& result){
        if(cur == NULL){
            return; 
        }
              
        Traversal(cur->left, result);
        Traversal(cur->right, result);
        result.push_back(cur->val);
    }
};

非递归

递归算法简单易懂,但是递归是要开辟栈帧的,栈的大小一般只有8M左右大小,递归调用层数太深很容易爆栈,因此我们还要学习如何通过非递归的方式进行遍历二叉树

非递归遍历也是大事化小的思想,拿前序遍历来讲,即先将左路节点全部入栈,root的所有左路节点入栈后,再进入到栈顶元素的右树(如果有的话),重复这个过程,即完成了前序遍历。

  • 前序
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        //前序遍历:根、左、右
        stack<TreeNode*> st;
        vector<int> v;

        TreeNode* cur = root;
        while(cur || !st.empty())
        {
            //开始访问一棵树
            //1.左路节点
            //2.左路节点的右子树
            while(cur)
            {
                v.push_back(cur->val);
                st.push(cur);
                cur = cur->left;
            }

            //开始访问右子树
            TreeNode* top = st.top();
            st.pop();

            //访问右子树,转换为子问题
            cur = top->right;
        }

        return v;
    }
};
  • 中序
    中序要按照左、根、右的方式遍历一棵树,还是一样一路并入root的左路节点,直到子问题变成左树为空的节点,这样直接将这个节点读取,再走他的右树(如果有右树的话),重复上面的逻辑即可。
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        //中序:左 根 右
        stack<TreeNode*> st;
        vector<int> v;

        TreeNode* cur = root;
        while(cur || !st.empty())
        {
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }

            //加入节点,因为左边一定是空了
            TreeNode* top = st.top();
            st.pop();
            v.push_back(top->val);
            
            //去右树
            cur = top->right;
        } 

        return v;
    }
};
  • 后序
    后序与前面两者略有不同,后序在什么时候能够读取节点呢?必须在左右树(节点)都访问完成后才可以访问根节点,我们是将左路节点一路压到栈里的,由于栈先进后出,也就是说走到任意一个节点,必然已经走过了他的左路节点。
    如果右树不存在,我们自然可以直接访问这个节点,但如果右树存在,我们还是要按照刚开始的步骤压入左路节点,这样肯定在这颗右树处理完毕后再访问到这个节点,这时候左右树都已访问完毕,可以访问这颗树了,因此需要一个指针来记录我们上个元素访问的是谁,如果是这个节点的右子树的话,就可以访问这个节点。
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        //后序:左 右 根
        stack<TreeNode*> st;
        vector<int> v;

        TreeNode* cur = root;
        TreeNode* prev = nullptr;
        while(cur || !st.empty())
        {
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }

            TreeNode* top = st.top();
            if(top->right == nullptr || top->right == prev)
            {
                v.push_back(top->val);
                st.pop();

                prev = top;
            }
            else
            {
                cur = top->right;
            }
        }

        return v;
    }
};

层序遍历

层序遍历就是一层一层遍历二叉树,如图:
在这里插入图片描述
要实现层序遍历我们要借助栈的数据结构,基本思路为:
先将根节点压入栈,访问完一个节点就将这个节点的子节点都入队列(如果有的话),以此类推,每次出队都会带来两个入队列。这一层出完,剩下的就都是下一层的了。

class Solution{
public:
    vector<vector<int>> levelOrder(TreeNode* root){
        vector<vector<int>> vv;
        int levelSzie = 0;/*表示当前层有多少个元素*/
        
        queue<TreeNode*> que;
        if(root)
        {
            que.push(root);
            levelSzie = 1;
        }
        while(!que.empty())
        {
            vector<int> v;
            while(levelSzie--)
            {
                TreeNode* node = que.front();
                que.pop();

                v.push_back(node->val);

                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            vv.push_back(v);
            levelSzie = que.size();
        }

        return vv;
    }
};

结语

以上就是本篇文章的全部内容了,希望对你理解二叉树的遍历有所帮助,我们下次再见~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝色学者i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值