二叉树的前中后序遍历的递归与迭代算法

一,递归算法
二叉树的前、中、后序遍历的思路和程序都非常简单。

前中后序遍历的算法结构相同,只需要调整打印的位置。

前序遍历:打印根-访问左子树-访问右子树
中序遍历:访问左子树-打印根-访问右子树
后序遍历:访问左子树-访问右子树-打印根

🔺遍历二叉树的算法中的基本操作是访问结点,所以不论是按照哪一种依次进行遍历,对有n个结点的二叉树,其时间复杂度为O(n).

🔺由于是该算法是递归调用,所需辅助空间是遍历过程中栈的最大容量,即树的深度;一般情况下为O(logn);最坏情况下树的深度为n,则此时空间复杂度为O(n)。

//前序遍历
vector<int> preorderTraversal(TreeNode* root) {
        vector<int> v;
        preorder(v,root);
        return v;
    }
    void preorder(vector<int>& v,TreeNode* root)
    {
        if(root!=nullptr)
            v.push_back(root->val);
        else
            return;
        preorder(v,root->left);
        preorder(v,root->right);
    }

二、迭代算法(1)
虽然递归算法的思路和程序都非常清晰,但其对栈空间有比较大的需求,当调用次数(即树的深度)达到一定量,此时便会出现栈空间不足的问题。

所以,我们便想采用非递归的算法来实现树的遍历。

因为树本身便是递归定义的,好像没有递归的结构还不太可以,因此我们应该想到用栈这个数据结构来模拟递归调用的过程,进而实现迭代算法的二叉树的遍历。

对于迭代算法,前序遍历和后续遍历的算法的程序结构是一样的。

🔺
前序:打印根-右子结点进栈-左子结点进栈——此时,由于栈具有先进后出的特点,得到的结果便是,根节点-左子节点-右子节点
后序:打印根-右子结点进栈-左子结点进栈——同样因为栈的先进后出的特点,得到的结果为,根节点-右子节点-左子节点。这时,得到的结果刚好是后序遍历的逆序,所以只需要对结果进行逆转就行了。

▲程序逻辑:
根不为空时压入栈
循环判断栈不为空时
弹出栈顶元素输出
右子节点压栈(前)/左子节点压栈(后)
左子节点压栈(前)/右子节点压栈(后)
反转结果(后)

//前序遍历

vector<int> preorderTraversal(TreeNode* root) {
        vector<int> v;
        stack<TreeNode*> s;
        if(root!=nullptr)
            s.push(root);
        TreeNode* temp=nullptr;
        while(!s.empty())
        {
            temp=s.top();
            s.pop();
            v.push_back(temp->val);
            if(temp->right!=nullptr)
                s.push(temp->right);
            if(temp->left!=nullptr)
                s.push(temp->left);

        }
        return v;
    }

//后序遍历

 vector<int> postorderTraversal(TreeNode* root) {
        //迭代算法
        vector<int> v;
        stack<TreeNode*> s;
        if(root!=nullptr)
            s.push(root);

        TreeNode* temp=nullptr;
        while(!s.empty())
        {
            temp=s.top();
            s.pop();
            v.push_back(temp->val);
            if(temp->left!=nullptr)
                s.push(temp->left);
            if(temp->right!=nullptr)
                s.push(temp->right);
        }
        reverse(v.begin(),v.end());
        return v;
    }

🔺中序遍历与前、后序遍历的程序结构就不同了

程序逻辑:
初始将根节点赋给中间变量temp
循环,当temp不为空或者栈不为空时,
如果temp不为空,将temp入栈并temp向左子树移动
否则,出栈打印并temp向右子树移动

//中序遍历

vector<int> inorderTraversal(TreeNode* root) {
        //迭代算法
        vector<int> v;

        stack<TreeNode*> s;
        TreeNode* temp=root;
        while(temp!=nullptr || !s.empty())
        {
            if(temp!=nullptr)
            {
                s.push(temp);
                temp=temp->left;
            }
            else
            {
                temp=s.top();
                v.push_back(temp->val);
                s.pop();
                temp=temp->right;
            }
        }
        return v;
    }

三、迭代算法(2)
由于以上的迭代算法中,对于二叉树的前、中、后序的遍历的算法结构一致。

因此,为了保持算法结构的一致方便学习和记忆。有一种算法通过将“压栈”和“输出”这些操作设计成结构体或类。压入栈的不是结点了,而变成了对结点的操作,听上去是挺有趣的算法。

此时,便可以像递归的遍历算法一样只需要改变打印、操作左子结点、操作右子结点三行代码的位置便可实现前、中、后序遍历。

(突然有点事情,代码下次再放,嘻嘻)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值