代码随想录算法训练营第十四天:二叉树-递归遍历、迭代遍历

day14:二叉树-递归遍历、迭代遍历

递归遍历

理论基础:

递归定义:

递归是定义一个对象或问题的方式,通过引用该对象或问题的较小版本来完成,比如斐波那契数列

递归调用:

递归调用是在函数或过程内部调用自身的行为。

底层实现:

递归的底层实现通常依赖于计算机的栈数据结构。当一个函数被调用时,计算机会将函数的局部变量、参数和返回地址等信息存储在栈中,这被称为栈帧。递归调用时,每次新的函数调用都会创建一个新的栈帧,将其推入栈中。

递归的基本执行过程:
  1. 当函数第一次被调用的时候,创建一个初始的栈帧,将参数和局部变量压入栈中,并存储函数的返回地址。
  2. 在函数内部,进行递归调用。这会导致新的栈帧被创建并压入栈中,其中包含了相同的函数的参数和局部变量。
  3. 递归调用会持续,每次都会创建一个新的栈帧,直到达到递归终止条件。这个条件通常在递归定义中明确定义,它告诉程序何时停止递归。
  4. 当递归达到终止条件后,函数开始回溯。回溯过程中涉及弹出栈帧,将局部变量和参数恢复到之前的状态,并跳回到存储的返回地址。
  5. 回溯过程一直持续到递归的最初调用处,然后整个递归调用序列才会完成。
注意事项:

递归调用的过程中会消耗栈空间,如果递归深度过大,可能会导致栈溢出错误。

递归三要素

  1. 确定递归函数的参数和返回值
  2. 确定终止条件
  3. 确定单层递归的逻辑

前/中/后序递归遍历:

按照三要素分析来确定递归:

1、确定递归函数的参数和返回值

参数:一个根节点和一个集合用于存放遍历过的数据

返回值:void

2、确定终止条件

递归遍历是深度优先搜索的一个过程,当沿着一个方向一直搜索到空节点的时候就往回

3、确定单层递归的逻辑

前序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,然后是左和右

中序遍历是左中右的循序,所以在单层递归的逻辑,是要先取左节点的数值,然后是中和右

后序遍历是左右中的循序,所以在单层递归的逻辑,是要先取左节点的数值,然后是右和中

前序递归遍历:中左右

LeetCode144.二叉树的前序遍历

public class Solution {
    public IList<int> PreorderTraversal(TreeNode root) {
        List<int> result = new List<int>();
        PreorderTraversalRecursive(root, result);
        return result;
    }
    //前序遍历:中左右
    public void PreorderTraversalRecursive(TreeNode node, List<int> result)
    {
        if(node == null)
            return;
        result.Add(node.val);//中
        PreorderTraversalRecursive(node.left, result);  //左
        PreorderTraversalRecursive(node.right, result); //右
    }
}
中序递归遍历:左中右

LeetCode 94.二叉树的中序遍历

//中序遍历
public class Solution {
    public IList<int> InorderTraversal(TreeNode root) {
        List<int> result = new List<int>();
        InorderTraversalRecursive(root, result);
        return result;
    }
    //中序遍历:左中右
    public void InorderTraversalRecursive(TreeNode node, List<int> result)
    {
        if(node == null)
            return;
        InorderTraversalRecursive(node.left, result);  //左
        result.Add(node.val);//中
        InorderTraversalRecursive(node.right, result); //右
    }
}
后序递归遍历:左右中

LeetCode 145.二叉树的后序遍历

//后续遍历
public class Solution {
    public IList<int> PostorderTraversal(TreeNode root) {
        List<int> result = new List<int>();
        PostorderTraversalRecursive(root, result);
        return result;
    }
    //后序遍历:左右中
    public void PostorderTraversalRecursive(TreeNode node, List<int> result)
    {
        if(node == null)
            return;
        PostorderTraversalRecursive(node.left, result);  //左
        PostorderTraversalRecursive(node.right, result); //右
        result.Add(node.val);//中
    }
}

迭代遍历

在递归中有提到,递归用到了栈的数据结构,每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以,也可以用栈来实现二叉树的前中后序遍历了。

前序迭代遍历:

LeetCode144.二叉树的前序遍历

前序遍历是中左右,处理步骤如下:

1、每次先处理的是中间节点,那么先将根节点放入栈中。

2、然后将右孩子加入栈。

3、再加入左孩子。

之所以先将右孩子加入栈,再加入左孩子,是因为我们采用的是栈的数据结构,栈是先入后出的,所以只有先加入右孩子,再加入左孩子,出栈的时候才能是前序遍历的中左右顺序。

public class Solution {
    public IList<int> PreorderTraversal(TreeNode root) {
        List<int> result = new List<int>();

        //根节点为空的情况
        if(root == null)
            return result;
        
        Stack<TreeNode> stk = new Stack<TreeNode>();
        stk.Push(root);
        while(stk.Count > 0)
        {
            TreeNode node = stk.Pop();  //中
            result.Add(node.val);
            if(node.right != null)      //右(空节点不需要入栈)
                stk.Push(node.right);
            if(node.left != null)       //左(空节点不需要入栈)
                stk.Push(node.left);
        }
        return result;
    }
}
中序迭代遍历:左中右

LeetCode 94.二叉树的中序遍历

中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点,导致访问的顺序和处理的顺序不一致,所以,在迭代法中对二叉树进行中序遍历的话,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

public class Solution {
    public IList<int> InorderTraversal(TreeNode root) {
        List<int> result = new List<int>();
        if(root == null)
            return result;

        TreeNode cur = root;
        Stack<TreeNode> stk = new Stack<TreeNode>();
        
        while(cur != null || stk.Count > 0)
        {
            //将当前节点及其左子节点入栈
            if(cur != null)
            {
                stk.Push(cur);
                cur = cur.left;//左
            }
            else
            {
                //弹出栈顶元素,即最左边的节点
                cur = stk.Pop();
                result.Add(cur.val);//中

                //处理右子树
                cur = cur.right;//右
            }
        }
        return result;
    }
}
后序递归遍历:左右中

LeetCode 145.二叉树的后序遍历

后序遍历中左右,因为先序遍历是中左右,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了。

public class Solution {
    public IList<int> PostorderTraversal(TreeNode root) {
        List<int> result = new List<int>();
        if(root == null)
            return result;
        
        Stack<TreeNode> stk = new Stack<TreeNode>();
        stk.Push(root);
        while(stk.Count > 0)
        {
            TreeNode node = stk.Pop();  //中
            result.Add(node.val);
            if(node.left != null)       //左(相对于前序遍历,这里更改顺序,左节点先入栈)
                stk.Push(node.left);
            if(node.right != null)      //右(空节点不需要入栈)
                stk.Push(node.right);
            
        }
        result.Reverse();//反转链表
        return result;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值