二叉树遍历

二叉树前序、中序、后序的递归遍历及非递归遍历

二叉树遍历

二叉树遍历是经常会遇到的数据结构面试题,小白在此分享一种通用方法。尽量让大家可以“一法通,万法通”

递归遍历

关于递归,可以首先假设已经有这样的一个现成的、和当下正在编写函数同型的函数可供我们调用,也就是可以在函数内部调用自己本身。因此,写递归函数的话,只需要缕清原问题和子问题的递归逻辑,以及递归终止条件(不然的话岂不是“子子孙孙无穷尽也了”?),就可以写出相应的代码啦。各种递归遍历方式的逻辑是一样的,这里以后序遍历为例,首先先要得到左子树的后序遍历结果,而后是右子树的后序遍历结果,最后再遍历当前节点。关于左、右子树的后序遍历结果到底是什么?不要忘了我们正在编写函数是干什么的?就是后序遍历树呀,所以只需要把左右子树当作出入参数传入我们的函数中,并将结果按后序的逻辑来整合就好,以下是递归遍历的C++代码。

class Solution {
public:
/*后序遍历*/
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res{};
        if(!root) return res;//递归终止条件
        vector<int> ls=postorderTraversal(root->left);
        res.insert(res.end(),ls.begin(),ls.end());
                
        ls=postorderTraversal(root->right);
        res.insert(res.end(),ls.begin(),ls.end());
        res.insert(res.end(),root->val);
        return res;
    }
};
class Solution {
public:
/*中序遍历*/
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res{};
        if(root==nullptr) return res;
        vector<int> ls=inorderTraversal(root->left);
        res.insert(res.end(),ls.begin(),ls.end());
                res.insert(res.end(),root->val);
        ls=inorderTraversal(root->right);
        res.insert(res.end(),ls.begin(),ls.end());
        return res;
    }
};
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res{};
        if(root==nullptr) return res;
        res.insert(res.end(),root->val);
        vector<int> ls=preorderTraversal(root->left);
        res.insert(res.end(),ls.begin(),ls.end());
        vector<int> rs=preorderTraversal(root->right);
        res.insert(res.end(),rs.begin(),rs.end());
        return res;
    }
};

迭代遍历

递归遍历是由系统维护一个递归栈,迭代遍历便是我们自己去显示维护栈。同样以后序遍历为例,首先不要忘记边界条件:如果输入为空树,直接返回一个空向量即可。
将根节点放入我们的堆栈,将迭代过程一般化,首先将当前节点从堆栈取出但不弹出,判断节点是否为空指针,如果非空,因为是后序遍历,读取顺序为左—右—根,所以入栈顺序正好相反,为根—右—左,由于此时根节点已经在栈顶,所以只需要先后将非空右节点、非空左节点放入即可,为了将遍历结果能正确存入向量,我们此处紧跟根节点之后填入一个空指针,后续判断时,一旦遇到空指针,我们就知道后续的数据是我们需要打印的啦。
接下来就是如果当前取出的节点是空指针是,那么我们就先弹出空指针,并在读取新的栈顶数据后弹出即可。
将整个过程循环,知道栈重新为空,则终止循环。以下是二叉树迭代后序遍历C++版代码。

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        if(!root) return {};
        vector<int> res{};
        stack<TreeNode* > TNs{};
        TNs.push(root);
        //迭代法
        while(!TNs.empty()){//直到栈空
            TreeNode* cur=TNs.top();
            if(cur){//人为在适当的位置放入空节点
                TNs.push(nullptr);
                if(cur->right) TNs.push(cur->right);
                if(cur->left) TNs.push(cur->left);
 
                            
            }else{
                TNs.pop();
                res.insert(res.end(),TNs.top()->val);
                TNs.pop();            
            }

        }
        return res;
    }
};

对于中序遍历,只需要修改上面当前节点非空时填入堆栈的顺序即可。因为是中序遍历,读取顺序为左—根—右,所以入栈顺序正好相反,为右—根—左,由于此时根节点已经在栈顶,所以需要先弹出根节点,而后将非空右节点填入堆栈,之后再将根节点填入,同样,为了将遍历结果能正确存入向量,我们此处紧跟根节点之后填入一个空指针,后续判断时,一旦遇到空指针,我们就知道后续的数据是我们需要打印的啦。最后将非空左节点放入。以下是中序遍历时,填入堆栈的关键代码,其他部分同后序遍历一致。

				TNs.pop();
                if(cur->right) TNs.push(cur->right);
                TNs.push(cur);
                TNs.push(nullptr);
                if(cur->left) TNs.push(cur->left);   

对于前序遍历,同样只需要修改上面当前节点非空时填入堆栈的顺序即可。因为是前序遍历,读取顺序为根—左—右,所以入栈顺序正好相反,为右—左—根,由于此时根节点已经在栈顶,所以需要先弹出根节点,而后依次将非空右节点、非空左节点填入堆栈,最后再将根节点填入,同样,为了将遍历结果能正确存入向量,我们此处紧跟根节点之后填入一个空指针,后续判断时,一旦遇到空指针,我们就知道后续的数据是我们需要打印的啦。最后。以下是前序遍历时,填入堆栈的关键代码,其他部分同后序遍历一致。

				TNs.pop();
                if(cur->right) TNs.push(cur->right);
                if(cur->left) TNs.push(cur->left);
                TNs.push(cur);
                TNs.push(nullptr);  

最后把用队列实现二叉树层次遍历的代码也附上吧,关键是扩容条件的决定。

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(!root) return {};
        vector<vector<int> > res{};
        queue<TreeNode* > TNq;
        TNq.push(root);
        while(!TNq.empty()){
            int siz=TNq.size();//利用队列大小来决定是否扩容
            vector<int> temp{};
            for(int i=0;i<siz;i++){
                TreeNode* cur=TNq.front();
                temp.push_back(cur->val);
                TNq.pop();
                if(cur->left) TNq.push(cur->left);
                if(cur->right) TNq.push(cur->right);
            }
            res.push_back(temp);
        }
        return res;
    }
};

欢迎大家批评指正!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值