二叉树的前中后序遍历分析(非递归实现)

本文详细介绍了如何非递归地进行二叉树的前序、中序和后序遍历。通过具体示例和代码解释了每个遍历过程,包括利用栈的数据结构来辅助完成遍历操作。对于前序遍历,从根节点入栈,每次弹出栈顶元素并访问;中序遍历从根节点出发,向左子树查找直至为空,然后访问栈顶元素并转向右子树;后序遍历则需要判断栈顶元素是否满足访问条件,确保正确顺序访问节点。
摘要由CSDN通过智能技术生成

二叉树的前中后序遍历(非递归实现)

之前我们已经提到过了二叉树的前中后序三种遍历方式,今天我们将采用非递归的方式来实现。

1. 前序遍历

我们需要借助 这种数据结构来达到 递归 的目的

1.1 分析

  • 首先我们将 根节点 入栈
  • 循环取栈顶元素同时出栈,并将栈顶元素添加到结果集合中
    • 判断当前节点的右孩子是否为空,非空入栈
    • 判断当前节点的左孩子是否为空,非空入栈

1.2 举例说明

下面我们通过一个具体的例子来说明,如下图所示

首先我们将根节点A入栈,然后进入循环

  • 首先打印A
  • 接着判断其右孩子( C)是否为空,发现其不为空,将其入栈
  • 接着判断其左孩子(B)是否为空,发现其不为空,将其入栈

第二次进入循环

  • 取栈顶元素B,打印B
  • 接着判断其右孩子(E)是否为空,发现其不为空,将其入栈
  • 接着判断其左孩子(D)是否为空,发现其不为空,将其入栈

第三次进入循环

  • 取栈顶元素D,打印D
  • 接着判断其右孩子(null)是否为空,发现为空
  • 接着判断其左孩子(null)是否为空,发现为空

第四次进入循环

  • 取栈顶元素E,打印E
  • 接着判断其右孩子(null)是否为空,发现为空
  • 接着判断其左孩子(null)是否为空,发现为空

第五次进入循环

  • 取栈顶元素C,打印C
  • 接着判断其右孩子(null)是否为空,发现为空
  • 接着判断其左孩子(null)是否为空,发现为空

循环结束,依次打印结果为:A B D E C

image-20210724193801346

3. 代码实现

 public List<Integer> preorderTraversal4(TreeNode root)
    {
        ArrayList<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        if (root == null)
        {
            return list;
        }
        //首先将根节点入栈
        stack.push(root);
        //循环判断,当栈不为空
        while (!stack.isEmpty())
        {
            //弹出栈顶元素
            TreeNode cur = stack.pop();
            //将其值添加到list集合
            list.add(cur.val);
            //如果当前节点的右孩子不为空,将其压栈
            if (cur.right != null)
            {
                stack.push(cur.right);
            }
            //如果当前节点的左孩子不为空,将其压栈
            if (cur.left != null)
            {
                stack.push(cur.left);
            }
        }
        return list;
    }

2. 中序遍历

2.1 分析

  • 借助循环从根节点出发,依次向左子树查找,遇到的所有元素全部入栈,直至为空,退出循环
    • 到达最左侧元素后,取栈顶元素同时出栈,将其添加到结果集合
    • 把刚得到的栈顶元素的右子树作为起点,再次循环向左找,路径上的元素全部入栈

2.2 举例说明

下面我们通过一个具体的例子来说明,如下图所示

image-20210724193801346
  • 首先,我们借助循环一直向左子树查找直至空,依次将A B D入栈**(此时栈:A B D)**

  • 接着取栈顶元素D,打印D (此时栈:A B)

  • 把栈顶元素 D 的右子树null 作为起点,发现为空

  • 接着取栈顶元素B,打印B (此时栈:A )

  • 把栈顶元素 B 的右子树E作为起点,将其入栈**(此时栈:A E)**

  • 接着取栈顶元素E,打印E (此时栈:A )

  • 把栈顶元素 E 的右子树null 作为起点,发现为空

  • 接着取栈顶元素A,打印A**(此时栈空 )**

  • 把栈顶元素 A 的右子树C作为起点,将其入栈 (此时栈:C )

  • 接着取栈顶元素C,打印C (此时栈空 )

    栈空结束

2.3 代码

public List<Integer> inorderTraversal2(TreeNode root)
{
    ArrayList<Integer> list = new ArrayList<>();
    if (root == null)
    {
        return list;
    }
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (true)
    {
        //循环遍历向左找,直到为空
        while (cur != null)
        {
            stack.push(cur);
            cur = cur.left;
        }
        //2.如果为空,遍历结束
        if (stack.isEmpty())
        {
            break;
        }
        //3.取栈顶元素并访问
        TreeNode pop = stack.pop();
        list.add(pop.val);
        //4.从当前节点右子树出发
        cur = pop.right;
    }
    return list;
}

3. 后序遍历

3.1 分析

  • 借助循环从根节点出发,依次向左子树查找,遇到的所有元素全部入栈,直至为空,退出循环
  • 判断栈顶元素(peek不是pop)是否能访问(一下两种情况可以访问)
    • 栈顶元素没有右子树,将其pop
    • 栈顶元素的右子树已经被访问过一次,将其pop
  • 把刚得到的栈顶元素的右子树作为起点,再次循环向左找,路径上的元素全部入栈

3.2 举例说明

下面我们通过一个具体的例子来说明,如下图所示

image-20210724193801346
  • 首先,我们借助循环一直向左子树查找直至空,依次将A B D入栈(此时栈:A B D)
  • 接着判断栈顶元素D,发现其没有右子树,将其出栈并打印
  • 把栈顶元素 D 的右子树null 作为起点,发现为空
  • 接着判断栈顶元素B,发现其存在右子树,但是还没访问过,将其右子树作为起点开始循环向左入栈(此时栈:A B E)
  • 接着判断栈顶元素E,发现其没有右子树,将其出栈并打印
  • 接着判断栈顶元素B**(第二次访问)**,发现其存在右子树,但是已经访问过,将其出栈并打印(此时栈:A )
  • 接着判断栈顶元素A,发现其存在右子树,但是还没访问过,将其右子树作为起点开始循环向左入栈(此时栈:A C)
  • 接着判断栈顶元素C,发现其没有右子树,将其出栈并打印(此时栈:A )
  • 接着判断栈顶元素A**(第二次访问)**,发现其存在右子树,但是已经访问过,将其出栈并打印(此时栈空 )
  • 结束

3.3 代码

public List<Integer> postorderTraversal(TreeNode root)
    {
        ArrayList<Integer> list = new ArrayList<>();
        if (root == null)
        {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode pre = null;
        while (true)
        {
            //1.依次向左查找入栈,直至空
            while (cur != null)
            {
                stack.push(cur);
                cur = cur.left;
            }
            if (stack.isEmpty())
            {
                break;
            }
            //拿出栈顶元素,检查能否访问
            TreeNode top = stack.peek();
            //需要判断的两个条件
            if (top.right == null || pre == top.right)
            {
                TreeNode pop = stack.pop();
                list.add(pop.val);
                //记录遍历完元素后的最后一个
                pre = pop;
            }
            //不满足条件的话把刚得到的栈顶元素的右子树作为起点,再次循环向左找,路径上的元素全部入栈
            else
            {
                cur = top.right;
            }
        }
        return list;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值