二叉树递归修炼【下】

在这里插入图片描述


之前的前中后序遍历是递归实现的,都很简单,下面以题代练一下

非递归实现前、中、后序遍历

1. 二叉树的前序遍历

题目描述:144. 二叉树的前序遍历

1.1 题目描述

image-20220426200404310

/**
 * 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) {}
 * };
 */

1.2 顺藤摸瓜

题目解析就不需要了,这前中后序谁都懂,关键是达到非递归的思想,已解决可能产生的栈溢出问题
要用非递归走前序,以前我们分成左子树和右子树根来看待,现在我们将其分为左路节点左路节点上的右子树

利用栈我们可以存储左子树节点,然后用循环访问他的左路节点上的右子树

image-20220426203026325

1.3 抽丝剥茧

先把所有的左子树节点放到栈中,然后依次访问当前cur的右子树节点,凡是访问到右子树节点,我就重新来cur去访问左路节点,直到cur走向空之后,pop掉stack中的top值,拿到下一个左路节点,这样的话我们利用栈的特性模拟了递归的操作,巧妙利用循环完成前序

Situation操作结束条件
大循环前序遍历所有节点cur和栈均走到空
左路节点循环左路节点走到底,依次push 进 stack左路全进,cur指向空
右路节点栈中拿一个左路节点,访问右路节点/
右路->左路右路节点为根,访问自己的左路左路全进,cur指向空

image-20220426210659915

1.4 手到拈来

    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> ret;
        TreeNode *cur= root;
        while(cur || !st.empty())//cur指向谁就表示开始前序访问这棵树
        {
            //1.访问左树节点,左路节点入栈
            while(cur)
            {
                ret.push_back(cur->val);
                st.push(cur);
                cur=cur->left;
            }
            //2.依次取出左路节点的右子树出来访问
            TreeNode* top = st.top();
            st.pop();
            //子问题形式访问右子树
            cur=top->right;
        }
        return ret;
    }

image-20220426205043947

2. 二叉树的中序遍历

题目描述:94. 二叉树的中序遍历

image-20220427143022178

2.1 抽丝剥茧

仿照前序遍历,我们可以实现中序遍历,中序类似的不就是先访问左子树,再访问左子树的右路

image-20220427143644494

2.2 手到拈来

    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> ret;
        TreeNode *cur= root;
        while(cur || !st.empty())//cur指向谁就表示开始前序访问这棵树
        {
            //1.访问左树节点,左路节点入栈
            while(cur)
            {
                st.push(cur);
                cur=cur->left;
            }
            //2.依次取出左路节点取出来时表示左子树已经访问完了
            //访问右子树
            TreeNode* top = st.top();
            st.pop();
            ret.push_back(top->val);

            cur=top->right;
        }
        return ret;
    }

image-20220427143931896

#3. 二叉树的后序遍历

题目描述:145. 二叉树的后序遍历

image-20220427144447698

3.1 抽丝剥茧

还是一样的,思考一下,父节点应该是至少要被访问多次,入栈之后从左子树回来有一次,再去右子树访问,直到第二次访问回到父亲节点的时候才能push_back进入vector

因此可以借助一个办法来识别第一次和第二次访问,如果第二次访问了说明上一个访问的节点就是右子树

那么只需要有一个prev指针,指向上一个被pop掉的节点就可以了

3.2 手到拈来

    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> ret;
        TreeNode *cur= root;
        TreeNode* prev= nullptr;
        while(cur || !st.empty())//cur指向谁就表示开始前序访问这棵树
        {
            //1.访问左树节点,左路节点入栈,但是暂时不能访问
            while(cur)
            {
                st.push(cur);
                cur=cur->left;
            }
            //2.去到一个栈顶元素,左路节点已经访问完了,去到了之后不能pop
            //如果右路已经访问完了才可以访问当前父亲
            TreeNode* top = st.top();
            //3.要访问根节点要么右干脆是空的,或者是prev指向right
            if(top->right==nullptr || top->right == prev)
            {
                st.pop();
                ret.push_back(top->val);
                prev=top;//prev指向这个访问过的节点
            }
            else
                cur=top->right;
            //否则访问右边,//vector存的是value,不是节点,所以设置一个指针
        }
        return ret;
}

4. 从前序与中序遍历序列构造二叉树

题目链接:105. 从前序与中序遍历序列构造二叉树

4.1 题目描述

image-20220427211808433

4.2 顺藤摸瓜

image-20220427220929995

给了前序和中序,而且题目说没有重复数字,那我们就可以构建一棵树了

我们一般可以通过前序确定根,然后通过中序确定左右子树的范围,然后递归确定左子树和右子树

image-20220428100734884

4.3 抽丝剥茧

开始分析代码怎么写,首先这道题目还是使用递归,递归就要考虑递归参数

4.3.1 递归参数

按照题目给的递归参数肯定是不够的,我们发现对于前序我们所需要的是找根,那就是一个指针就可以了,但是对于中序可以发现其实我们要的是区间,所以所需要两个指针,也就是如下,我们整一个子函数,用来递归

  _TreeNode* buildTree(vector<int>& preorder, int pi,vector<int>& inorder,int begin,int end) {
    
    }

同时我们要注意因为我们需要的是同一个pi在里面++移动,如果不加的话上一层的++不会影响下一层,所以说还是要传引用

  _TreeNode* buildTree(vector<int>& preorder, int& pi,vector<int>& inorder,int begin,int end) {
    
    }

4.3.2 代码思路

先用前序创建根,然后在中序中去找到相同的根节点,这样就能够划分左右区间

这里我们需要确定返回条件

image-20220428110148520

 if(in_begin> in_end)
            return nullptr;

注意不能取等于号,不然9就没有在该进的地方进去,等于表示还有一个值

4.4 手到拈来

    TreeNode* _buildTree(vector<int>& preorder, int& pi,
    vector<int>& inorder,int in_begin,int in_end) {
        if(in_begin> in_end)
            return nullptr;
        //前序创建根
        TreeNode*root= new TreeNode(preorder[pi]);
           ++pi;
        //确定根在中序的位置
        int rooti = in_begin;
        while(rooti<= in_end)
        {
            if(root->val == inorder[rooti])
                break;
            else
                rooti++;
        }
        //[int_begin,rooti-1]rooti[rooti+1,in_end]
        root->left=_buildTree(preorder,pi,inorder,in_begin,rooti-1);
        root->right=_buildTree(preorder,pi,inorder,rooti+1,in_end);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size()==0)
            return nullptr;
        int i=0;
        return _buildTree(preorder,i,inorder,0,inorder.size()-1);
    }

image-20220428111651083

5. 从中序与后序遍历序列构造二叉树

题目链接:106. 从中序与后序遍历序列构造二叉树

5.1 题目描述

image-20220429202136872

5.2 顺藤摸瓜

这道题目我们将使用与之前完全相同的思路,只需要修改一下前序与后序之间的不同即可,前序的时候给出的vector是左子树右子树根,现在后序的话是左子树右子树根

image-20220429205647845

我们还是可以通过后序确定根,然后通过中序确定左右子树的范围,然后递归确定左子树和右子树

5.3 抽丝剥茧

同样的写一个子函数

TreeNode* _buildTree(vector<int>& inorder, int in_begin,int in_end, vector<int>& postorder,int& pi) {
 }

然后当我们在中序找到根节点之后,按照–pi,在后序中应该是先遇到右子树,在遇到左子树,所以我们应该先往右树递归,再往左树递归

//[int_begin,rooti-1]rooti[rooti+1,in_end]
        root->right=_buildTree(inorder,rooti+1,in_end,postorder,pi);
        root->left=_buildTree(inorder,in_begin,rooti-1,postorder,pi);

5.4 手到拈来

    TreeNode* _buildTree(vector<int>& inorder, int in_begin,int in_end,
    vector<int>& postorder,int& pi) {
    if(in_begin> in_end)
        return nullptr;
    //后序创建根,根是最后一个
    TreeNode*root=new TreeNode(postorder[pi]);
    --pi;
    //确定根在中序的位置
    int rooti = in_begin;
    while(rooti<= in_end)
    {
        if(root->val == inorder[rooti])
            break;
        else
            rooti++;
    }
    //[int_begin,rooti-1]rooti[rooti+1,in_end]
        root->right=_buildTree(inorder,rooti+1,in_end,postorder,pi);
        root->left=_buildTree(inorder,in_begin,rooti-1,postorder,pi);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int i=postorder.size()-1;
        return  _buildTree(inorder,0,i,postorder,i);
    }

image-20220429205557392

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

言之命至9012

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

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

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

打赏作者

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

抵扣说明:

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

余额充值