Java 数据结构篇-二叉树的深度优先遍历(实现:递归方式、非递归方式)

news2025/6/9 23:35:39

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
  

文章目录

        1.0 二叉树的说明

        1.1 二叉树的实现

        2.0 二叉树的优先遍历说明

        3.0 用递归方式实现二叉树遍历

        3.1 用递归方式实现遍历 - 前序遍历

        3.2 用递归方式实现遍历 - 中序遍历

        3.3 用递归方式实现遍历 - 后序遍历

        4.0 用非递归方式实现二叉树遍历

        4.1 用非递归方式实现遍历 - 前序遍历

        4.2 用非递归方式实现遍历 - 中序遍历

        4.3 用非递归方式实现遍历 - 后序遍历

        5.0 深度遍历的完整代码


        1.0 二叉树的说明

        二叉树是一种树形数据结构,其中每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树可以为空,或者包含一个根节点和两个指向左子树和右子树的指针。

        1.1 二叉树的实现

二叉树可以是空的,也可以是具有以下特点的非空树:

         - 每个节点最多有两个子节点,分别称为左子节点和右子节点

         - 左子树和右子树都是二叉树

代码实现如下:

public class MyBinaryTree {

    //指向左子树
    private MyBinaryTree left;
    //当前节点的值
    private int val;
    //指向右子树
    private MyBinaryTree right;

    //构造方法一:带一个值的参数
    public MyBinaryTree(int val) {
        this.val = val;
    }

    //构造方法二:带三个参数
    public MyBinaryTree(MyBinaryTree left, int val, MyBinaryTree right) {
        this.left = left;
        this.val = val;
        this.right = right;
    }

}

        实现两个构造方法是为了创建二叉树的时候会比较方便一些。

二叉树的创建如下:

MyBinaryTree myBinaryTree = new MyBinaryTree(new MyBinaryTree(new MyBinaryTree(4),2,null),
                                                        1,
                                             new MyBinaryTree(new MyBinaryTree(5),3,new MyBinaryTree(6)));

该二叉树的图片:

        2.0 二叉树的优先遍历说明

        二叉树的优先遍历是指按照一定顺序访问二叉树中的所有节点。常见的三种优先遍历方式包括:前序遍历、中序遍历和后序遍历。可以使用递归实现非递归实现这三种遍历方式。

               

        3.0 用递归方式实现二叉树遍历

        递归实现前序遍历、中序遍历、后续遍历。用递归实现这三种遍历方式的区别就是对于数据什么时候处理,如打印。

                - 前序遍历(Preorder Traversal):首先访问根节点,然后递归地遍历左子树,最后递归地遍历右子树。在前序遍历中,节点的访问顺序是“根-左-右”。

                - 中序遍历(Inorder Traversal):首先递归地遍历左子树,然后访问根节点,最后递归地遍历右子树。在中序遍历中,节点的访问顺序是“左-根-右”。

                - 后序遍历(Postorder Traversal):首先递归地遍历左子树,然后递归地遍历右子树,最后访问根节点。在后序遍历中,节点的访问顺序是“左-右-根”。

        3.1 用递归方式实现遍历 - 前序遍历

        具体思路为:在递出之前需要对当前节点的值访问,然后再接着向左子树递出,一直下去,每一次都需要先对当前节点的值访问,直到 node == null 时,左子树结束递出。当右子树此时也为  node == null 时,从叶子节点开始回归,回归到上一个节点的右子树。

代码实现如下:

假设该二叉树的图:

那么前序遍历打印的顺序为:124356

    //使用递归实现前序遍历
    public void preTraversalRecursion(MyBinaryTree node) {
        if (node == null) {
            return;
        }
        System.out.print(node.val + " ");
        preTraversalRecursion(node.left);
        preTraversalRecursion(node.right);
    }

        

        3.2 用递归方式实现遍历 - 中序遍历

        具体思路为:向左子树递出,一直下去,直到 node == null 时,左子树结束递出。再来对当前节点的值进行访问,接着继续向着右子树递出,当右子树此时也为 node == null 时,从叶子节点开始回归,回归到上一个节点的右子树前先对当前节点的值进行访问。

代码实现如下:

假设该二叉树的图:

中序遍历打印的顺序为:421563

    //使用递归实现中序遍历
    public void middleTraversalRecursion(MyBinaryTree node) {

        if (node == null) {
            return;
        }

        middleTraversalRecursion(node.left);
        System.out.print(node.val + " ");
        middleTraversalRecursion(node.right);

    }

        相对于前序遍历,中序遍历就是将对节点的值的访问进行换了一下位置。先进行左子树递归,再来访问值,最后再右子树递归。

        3.3 用递归方式实现遍历 - 后序遍历

        具体思路为:向左子树递出,一直下去,直到 node == null 时,左子树结束递出。再接着继续向着右子树递出,再来对当前节点的值进行访问,当右子树此时也为 node == null 时,从叶子节点开始回归,回归到对当前节点的值进行访问。

代码实现如下:

假设该二叉树的图:

后续遍历打印顺序为:425631

    //使用递归实现后续遍历
    public void postTraversalRecursion(MyBinaryTree node) {
        if (node == null) {
            return;
        }
        postTraversalRecursion(node.left);
        postTraversalRecursion(node.right);
        System.out.print(node.val + " ");
    }

        4.0 用非递归方式实现二叉树遍历

        非递归实现前序遍历、中序遍历、后续遍历。用非递归实现这三种遍历方式的区别是访问的顺序不同,而前中后序它们所走的路线是一致的

假设该树的图片:

        具体思路为:先定义一个当前节点 curr 一开始指向根节点,还需要有一个栈的数据结构来存储数据。从根节点开始往后先左子树一直遍历下去 curr = curr.left ,每次都需要存储当前节点到栈中,直到 curr == null 时,说明了左子树已经遍历完毕了。此时需要栈弹出节点,来寻找 “来时的路”,弹出来的节点就是离当前节点最近的节点,在原路返回的时候,还要判断当前节点是否还有右子树,将弹出栈的节点的右子树赋值给 curr = tp.right 。如果右子树不为 null ,就会继续对当前节点的左子树遍历,且把当前节点压入栈中,方便记住 “来时的路”,;如果右子树不为 null ,继续把栈中的节点弹出,直到栈中没有了节点且 curr == null 时,因此整个二叉树就遍历结束了。

        4.1 用非递归方式实现遍历 - 前序遍历

        对于前序遍历来说,就是在来到下一个节点之前对当前节点的值先访问了。

代码如下:

    //非递归实现前序遍历
    public void preTraversal(MyBinaryTree node) {
        MyBinaryTree curr = node;
        LinkedList<MyBinaryTree> stack = new LinkedList<>();

        while (curr != null || !stack.isEmpty()) {
            if (curr != null) {
                System.out.print(curr.val + " ");
                stack.push(curr);
                curr = curr.left;
            }else {
                MyBinaryTree tp = stack.pop();
                curr = tp.right;
            }
        }
        System.out.println();

    }

        4.2 用非递归方式实现遍历 - 中序遍历

        对于中序遍历来说,在栈弹出节点后,对弹出的节点进行访问。

代码如下:

    //非递归实现中序遍历
    public void middleTraversal(MyBinaryTree node) {
        MyBinaryTree curr = node;
        LinkedList<MyBinaryTree> stack = new LinkedList<>();
        while ( curr != null || !stack.isEmpty()) {

            if (curr != null) {
                stack.push(curr);
                curr = curr.left;
            }else  {
                MyBinaryTree tp = stack.pop();
                System.out.print(tp.val + " ");
                curr = tp.right;
            }
        }
        System.out.println();

    }

        4.3 用非递归方式实现遍历 - 后序遍历

        相比前面两种前中后序遍历,后序遍历会多了一个判断,由于先访问完当前节点的左子树和右子树后,才能对当前节点的值进行访问,所以不能访问完左子树后,立马将当前节点弹出栈,还需要保留该节点,先对该节点的右节点进行访问完毕之后,再来访问该节点的值,最后就可以弹出栈了。需要先判断,当前节点的右子树为 null 时,可以弹出当前节点或者已经对当前节点的右节点访问完毕之后,也可以将当前节点弹出。

        那么如何来判断是否访问完毕当前节点的右子树了?

        可以用一个变量来标记,只需要判断最近弹出栈的节点是否为当前节点的右子树节点即可。

代码如下:

    //非递归实现后序遍历
    public void postTraversal(MyBinaryTree node) {
        MyBinaryTree crr = node;
        LinkedList<MyBinaryTree> stack = new LinkedList<>();
        MyBinaryTree pop = null;//最后一次弹栈元素
        while ( crr != null || !stack.isEmpty()) {
            if (crr != null) {
                stack.push(crr);
                crr = crr.left;
            }else {
                MyBinaryTree tp = stack.peek();
                if ( tp.right == null || tp.right == pop ) {
                    pop = stack.pop();
                    System.out.print(pop.val + " ");
                }else {
                    crr = tp.right;
                }

            }
        }
    }

        5.0 深度遍历的完整代码

import java.util.LinkedList;

public class MyBinaryTree {

    //指向左子树
    private MyBinaryTree left;
    //当前节点的值
    private int val;
    //指向右子树
    private MyBinaryTree right;

    //构造方法一:带一个值的参数
    public MyBinaryTree(int val) {
        this.val = val;
    }

    //构造方法二:带三个参数
    public MyBinaryTree(MyBinaryTree left, int val, MyBinaryTree right) {
        this.left = left;
        this.val = val;
        this.right = right;
    }


    //使用递归实现前序遍历
    public void preTraversalRecursion(MyBinaryTree node) {
        if (node == null) {
            return;
        }
        System.out.print(node.val + " ");
        preTraversalRecursion(node.left);
        preTraversalRecursion(node.right);
    }

    //使用递归实现中序遍历
    public void middleTraversalRecursion(MyBinaryTree node) {

        if (node == null) {
            return;
        }

        middleTraversalRecursion(node.left);
        System.out.print(node.val + " ");
        middleTraversalRecursion(node.right);

    }

    //使用递归实现后续遍历
    public void postTraversalRecursion(MyBinaryTree node) {
        if (node == null) {
            return;
        }
        postTraversalRecursion(node.left);
        postTraversalRecursion(node.right);
        System.out.print(node.val + " ");
    }


    //非递归实现前序遍历
    public void preTraversal(MyBinaryTree node) {
        MyBinaryTree curr = node;
        LinkedList<MyBinaryTree> stack = new LinkedList<>();

        while (curr != null || !stack.isEmpty()) {
            if (curr != null) {
                System.out.print(curr.val + " ");
                stack.push(curr);
                curr = curr.left;
            }else {
                MyBinaryTree tp = stack.pop();
                curr = tp.right;
            }
        }
        System.out.println();

    }

    //非递归实现中序遍历
    public void middleTraversal(MyBinaryTree node) {
        MyBinaryTree curr = node;
        LinkedList<MyBinaryTree> stack = new LinkedList<>();
        while ( curr != null || !stack.isEmpty()) {

            if (curr != null) {
                stack.push(curr);
                curr = curr.left;
            }else  {
                MyBinaryTree tp = stack.pop();
                System.out.print(tp.val + " ");
                curr = tp.right;
            }
        }
        System.out.println();

    }


    //非递归实现后序遍历
    public void postTraversal(MyBinaryTree node) {
        MyBinaryTree crr = node;
        LinkedList<MyBinaryTree> stack = new LinkedList<>();
        MyBinaryTree pop = null;//最后一次弹栈元素
        while ( crr != null || !stack.isEmpty()) {
            if (crr != null) {
                stack.push(crr);
                crr = crr.left;
            }else {
                MyBinaryTree tp = stack.peek();
                if ( tp.right == null || tp.right == pop ) {
                    pop = stack.pop();
                    System.out.print(pop.val + " ");
                }else {
                    crr = tp.right;
                }

            }
        }
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1285552.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;博主最近一直在钻研动态规划算法&#xff0c;最近在Leetcode上刷题的时候遇到一个Hard难度的动态规划题&#xff0c;今天就借此机会来给大家分享…

Python的requests库实现HTTPS

嘿&#xff0c;Python程序员们&#xff01;今天我们要来点刺激的——使用Python的requests库实现HTTPS请求&#xff01;是的&#xff0c;你没有听错&#xff0c;我们要一起迈入HTTPS的神秘世界&#xff01; 首先&#xff0c;我们来了解一下HTTPS是什么。HTTPS是HTTP Secure的缩…

cmd下查看python命令的用法

在cmd下&#xff0c;可以运行python --help或者py --help来查看python命令的用法。例如&#xff1a;

LeetCode [中等]全排列(回溯算法)

46. 全排列 - 力扣&#xff08;LeetCode&#xff09; 回溯法 采用试错的思想&#xff0c;它尝试分步的去解决一个问题。在分步解决问题的过程中&#xff0c;当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候&#xff0c;它将取消上一步甚至是上几步的计算&…

Qt应用开发(Quick篇)——布局类与布局模块

一、前言 实际 应用中&#xff0c;布局是常用的功能&#xff0c;布局最直观的就是提供空间使用率&#xff0c;改善空间的流动和模块之间的重叠&#xff0c;让界面更加的美观。 二、布局类Layout 2.1 介绍 将Layout类型的对象附加到布局的子元素上&#xff0c;提供有关该项的特…

有趣的代码——有故事背景的程序设计2

有趣的代码是很多的&#xff0c;所以接着上一篇&#xff0c;这一篇再和大家分享一些有故事背景的程序设计。 目录 1.百元买百鸡问题 2.哥德巴赫猜想 3.折半查找 4.主对角线元素之和 5.戈尼斯堡七桥问题 1.百元买百鸡问题 已知公鸡5元一只&#xff0c;母鸡3元一只&#xf…

【代码随想录刷题】Day20 二叉树06

文章目录 1.【654】最大二叉树1.1 题目描述1.2 解题思路1.3 java代码实现1.4 总结 2.【617】合并二叉树2.1 题目描述2.2 解题思路2.3 java代码实现 3.【700】二叉搜索树中的搜索3.1 题目描述3.2 解题思路3.3 java代码实现 4.【98】验证二叉搜索树4.1 题目描述4.2 解题思路4.3 j…

Hadoop学习笔记(HDP)-Part.02 核心组件原理

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

Python代码编译并生成Docker镜像

Python代码编译并生成Docker镜像 前言 实际python项目交付时往往有针对关键代码进行保护的需求&#xff0c;本文介绍了一种简单可行的方案&#xff1a;1. 在Linux系统上先将 .py 文件编译为 .so 文件&#xff0c;2. 将整个项目打包成Docker镜像&#xff08;解决 .so 文件的环…

Mars3d支持geoserver的rest服务类型数据渲染上图

需求&#xff1a;geoserver的rest服务类型的矢量数据通过mars3d的引擎直接渲染上图 学习过程&#xff1a; 1.通过全局查询示例的map.js文件&#xff0c;可以看到示例调用的rest服务类型&#xff0c;发现很多wfs接口的数据直接上图渲染矢量数据以及query接口下面调用这个服务的…

python-单词本|通讯录

编写程序&#xff0c;生词本。 def sayHello():print("" * 20 \n 欢迎使用生词本\n 1.查看生词本\n 2.背单词\n 3.添加新单词\n 4.删除单词\n 5.清空生词本\n 6.退出生词本\n * 20 \n)def addW(data):word input("请输入新单词&#xff1a;")trans i…

成为Java开发高手:掌握Spring框架的关键技能-DI

DI相关内容 1.1 setter注入1.1.2 注入引用数据类型1.1.3 注入简单数据类型步骤1:声明属性并提供setter方法步骤2:配置文件中进行注入配置步骤3:运行程序 1.2 构造器注入1.2.2 构造器注入引用数据类型步骤1:删除setter方法并提供构造方法步骤2:配置文件中进行配置构造方式注入步…

docker搭建xxl-job

使用docker-compose创建并运行xxl-job 查看、下载镜像 docker search xxl-job # 结果&#xff0c;自己指定版本 xuxueli/xxl-job-admin:2.3.1创建文件夹 /usr/local/software/xxl-job/logs编排docker-compose文件 version: 2 networks:wn_docker_net:external: true servic…

阿里云服务器租赁价格表,预算100元到5000元可选配置

阿里云服务器租用费用&#xff0c;阿里云轻量应用服务器2核2G3M带宽轻量服务器一年87元&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;ECS云服务器e系列2核2G配置3M固定带宽99元一年、2核4G配置365元一年、2核8G配置522元一年&#xff0c;阿里云u1服务器2核4G、…

Hadoop学习笔记(HDP)-Part.10 创建集群

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

如何通过DB操作地理空间数据

从公众号转载&#xff0c;关注微信公众号掌握更多技术动态 --------------------------------------------------------------- 一、PostgreSQL PostgreSQL是一个强大的对象关系数据库管理系统&#xff08;ORDBMS&#xff09;。它是在BSD风格的许可下发布的&#xff0c;因此是自…

2023年【危险化学品经营单位安全管理人员】免费试题及危险化学品经营单位安全管理人员复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员免费试题是安全生产模拟考试一点通生成的&#xff0c;危险化学品经营单位安全管理人员证模拟考试题库是根据危险化学品经营单位安全管理人员最新版教材汇编出危险化学品经营单位安全管…

07、pytest指定要运行哪些用例

官方用例 # 目录结构 | |----test_mod.py | |----testing||----test_dir.py# content of test_mod.py import pytestdef func(x):return x 1def test_mod():print("test_mod function was invoked")assert func(3) 5def test_func():print("test_func was in…

Cyanine7-NHS ester荧光染料的化学结构、光谱性质和荧光特性

Cyanine7-NHS ester的结构包括一个靛菁环结构和一个NHS ester活性基团。NHS ester官能团是一种活化基团&#xff0c;用于将染料共价结合到含有游离氨基官能团的生物分子上。 **光谱性质&#xff1a;**Cyanine7-NHS ester的光谱性质通常包括&#xff1a; **激发波长&#xff08…

Hadoop学习笔记(HDP)-Part.16 安装HBase

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …