算法33:二叉树的前序遍历(递归、迭代、Morris)

一、需求

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

在这里插入图片描述

输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

在这里插入图片描述

输入:root = [1,2]
输出:[1,2]

示例 5:

在这里插入图片描述

输入:root = [1,null,2]
输出:[1,2]

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

**进阶:**递归算法很简单,你可以通过迭代算法完成吗?

二、思路分析图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、代码

(一)公共代码(树节点类)

package com.bessky.pss.wzw.SuanFa;

import java.util.ArrayList;
import java.util.List;

/**
 * 树节点类
 *
 * @author 王子威
 * @date 2022/7/27
 */
public class TreeNode
{
    int val;

    TreeNode left;

    TreeNode right;

    TreeNode()
    {
    }

    TreeNode(int val)
    {
        this.val = val;
    }

    TreeNode(int val, TreeNode left, TreeNode right)
    {
        this.val = val;
        this.left = left;
        this.right = right;
    }

    /**
     * 遍历显示
     *
     * @return
     */
    public String display()
    {
        // 创建存储先序遍历后的结果
        List<Integer> results = new ArrayList<>();

        // 调用:递归方法
        this.inorder(this, results);

        return results.toString();
    }

    /**
     * 递归方法体
     *
     * @param root
     * @param results
     */
    public void inorder(TreeNode root, List<Integer> results)
    {
        if (root == null)
        {

            return;
        }

        // 调用:当最左时就存储 || 或左没有时存储
        results.add(root.val);
        // 调用:递归方法(先把最左边的拿到)
        inorder(root.left, results);

        // 调用:递归方法(左没有时就递归右边)
        inorder(root.right, results);
    }
}

(二)数据初始化

/**
 * 入口
 *      144、二叉树的前序遍历
 * 输入:
 *      [1,null,2,3]
 * 输出:
 *      [1,2,3]
 * 解释:
 *      1.递归方案
 *      2.迭代方案
 */
@Test
public void suanfa33()
{
    // 初始化
    TreeNode root = new TreeNode(1, null, new TreeNode(2, new TreeNode(3), null));

    // 打印
    List<Integer> result1 = new ArrayList<>();
    this.recursionPreorderTraversal(root, result1);
    System.out.println("递归方案 = " + result1.toString());

    List<Integer> result2 = new ArrayList<>();
    this.iterationPreorderTraversal(root, result2);
    System.out.println("迭代方案 = " + result2.toString());

    List<Integer> result3 = new ArrayList<>();
    this.morrisPreorderTraversal(root, result3);
    System.out.println("morris方案 = " + result3.toString());
}

(三)递归方案

/**
 * 递归方案
 *
 * @param root
 * @param result
 */
private void recursionPreorderTraversal(TreeNode root, List<Integer> result)
{
    if (root == null)
    {
        return;
    }
    result.add(root.val);
    this.recursionPreorderTraversal(root.left, result);
    this.recursionPreorderTraversal(root.right, result);
}

(四)迭代方案

/**
 * 迭代方案
 *
 * @param root
 * @param result
 */
private void iterationPreorderTraversal(TreeNode root, List<Integer> result)
{
    // 如果root为 null
    if (root == null)
    {
        // 直接返回
        return;
    }

    // 将root的值加入到集合中
    result.add(root.val);
    // root赋值到临时对象中
    TreeNode temp = root;
    // 声明堆对象
    Stack<TreeNode> stack = new Stack<>();
    // 将root加入到堆对象
    stack.push(root);
    // 循环(条件:堆 不为 空)
    while (!stack.isEmpty())
    {
        // 获取顶点节点
        TreeNode peek = stack.peek();
        // 获取顶节点左子节点
        TreeNode left = peek.left;
        // 获取顶节点右子节点
        TreeNode right = peek.right;
        // 左节点 不等于 null && 左节点 不等于 临时节点 && 右节点 不等于 临时节点
        if (left != null && left != temp && right != temp)
        {
            // 添加左节点的值
            result.add(left.val);
            // 加入左节点到堆中
            stack.push(left);
        }
        // 否则 如果 (右节点 不为 null && 右节点 不等于临时节点)
        else if (right != null && right != temp)
        {
            // 添加右节点的值
            result.add(right.val);
            // 加入右节点到堆中
            stack.push(right);
        }
        // 否则
        else
        {
            // 将顶节点赋值到临时节点
            temp = peek;
            // 清除栈的顶节点
            stack.pop();
        }
    }
}

(五)Morris方案

/**
 * Morris方案
 *
 * @param root
 * @param result
 */
private void morrisPreorderTraversal(TreeNode root, List<Integer> result)
{
    // root如果为空
    if (root == null)
    {
        // 直接返回
        return;
    }

    // 声明p1存放root,声明p2
    TreeNode p1 = root, p2;

    // 循环(条件:p1 不等于 null)
    while (p1 != null)
    {
        // 将左节点给p2
        p2 = p1.left;

        // 如果p2 不等于 null
        if (p2 != null)
        {
            // 循环(条件:p2右节点 不等于 null && p2右节点 不等于 p1)
            while (p2.right != null && p2.right != p1)
            {
                // 将p2右节点赋值到p2节点
                p2 = p2.right;
            }

            // p2右节点 等于null
            if (p2.right == null)
            {
                // 添加p1的值
                result.add(p1.val);
                // 将p1 赋值到p2右节点(这里应该是最右节点)
                p2.right = p1;
                // p1左节点赋值到p1
                p1 = p1.left;
                continue;
            }
        }
        else
        {
            // 添加p1的值
            result.add(p1.val);
        }
        // p1右节点赋值p1
        p1 = p1.right;
    }
}

(六)结果图

在这里插入图片描述

作者:王子威

四、总结

  • 学习了二叉树的前序遍历
  • 递归方案技巧:前序遍历(添加放前面),中序遍历(添加放中间),后序遍历(添加放后面)
  • Morris还是不能直接默写,感觉理解的还是不透彻
  • 迭代一开始蒙的,完全忘记了使用Stack堆的特性
  • Stack堆,先进先出,和队列是同样的,所以这里队列也可以做
  • 算法兴趣+1 总:33
  • 加强了对算法的分析能力
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值