二叉树的后序遍历问题的具体思路 递归法 迭代法(附完整代码)


前言

本文将主要围绕以下问题进行讨论:

二叉树的后序遍历。

本文将分别以两种方法【递归】【迭代】来解决这一问题(文章末尾附完整代码)


一、如何理解后序遍历?

二叉树的后序遍历是一种常见的遍历方式,主要按照 “左子树 - 右子树 - 根节点” 的顺序访问二叉树的每个节点。

二、方法一(递归法)

1.递归左右子节点

递归需要不断地重复调用同一函数,只改变传入节点,直到将所有节点全部遍历。

而在“改变传入节点”这一环节中,依照什么来改变呢?

我们可以通过每次传入当先节点的子节点,也就是先传左子节点,再传右子节点。在这个过程中用vector记录下这个顺序。

代码如下:

class Solution {
public:
    vector<int> tre;
    void digui(TreeNode* root){//左右根
        if(root==nullptr){
            return ;
        }
        if(root->left!=nullptr){
            digui(root->left);
            tre.push_back(root->left->val);
        }
        if(root->right!=nullptr){
            digui(root->right);
            tre.push_back(root->right->val);
        }     
    }
    vector<int> postorderTraversal(TreeNode* root) {
        if(root==nullptr){
            return tre;
        }
        else{
            digui(root);
            tre.push_back(root->val);
            return tre;            
        }
    }
};

值得注意的是:在所有的递归完成后,需要再将最初的根节点传进去。因为在这个逻辑下,我们是传入一个父节点下面的两个子节点,但是最初的根节点是没有父节点的,我们无法在递归中传入。因此只能最后在递归完成后,单独把最初根节点传进去。

2.递归根节点

相比于前一种递归方法来说,递归根节点就容易很多啦。

我们只需要先递归左,再递归右,传入节点本身就可以啦(依照左 - 右 - 根)

代码如下:

class Solution {
public:
    vector<int> tre;
    void digui(TreeNode* root) {
        if (root == nullptr) return;
        digui(root->left);
        digui(root->right);
        tre.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        digui(root);
        return tre;
    }
};

这种方法更加简明,同时也不需要在完成递归后,传入根节点补全向量。


三、方法二(迭代法)

这个迭代实现的后序遍历算法主要利用了栈来模拟递归的过程。

其核心思想是在遍历过程中,先尽可能地深入到左子树,当无法继续深入左子树时,检查当前节点的右子树是否已经被访问过,如果右子树未被访问,则转向右子树继续遍历;如果右子树已被访问或不存在右子树,则访问当前节点并将其从栈中弹出。

完整代码如下:(已标注好每句的作用)

/**
 * 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> tre;//
    stack<TreeNode*> sta;//建辅助栈
    TreeNode* rot;//创建节点,用于后续
    void diedai(TreeNode* root) {//创建迭代函数,传入节点,无返回值,因为我们是靠栈来存储的
        if (root == nullptr){//如果是空树,直接跳出函数
            return ;
        }
        while (root!= nullptr ||!sta.empty()) {//进入while循环
            if (root!= nullptr) {//节点不为空,说明还没搜索到树的末端
                sta.push(root);//将当前节点压入栈
                root = root->left;//使节点指向左子节点
            }//目的就是一直向树的左下方走
             else {//说明此时root==nullptr,这时最左侧已经走到末端了
                TreeNode* rooot = sta.top();//新建一个节点,用来取栈顶元素
                if (rooot->right!= nullptr && rooot->right!=rot) {//如果这个栈顶元素的右侧还有节点,并且没有遍历过
                    root = rooot->right;//回溯,将节点指向右子节点
                } else {//如果右侧无节点,或者已经遍历过
                    tre.push_back(rooot->val);//就直接把当前节点值送入vector中
                    rot = sta.top();//然后继续取下一个
                    sta.pop();//并且让它出栈
                }
            }
        }  
    }
    vector<int> postorderTraversal(TreeNode* root) {
        diedai(root);
        return tre;
    }
};

时间复杂度为O(n)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值