Java实现二叉树的基本操作详解

目录
  • 1. 二叉树结点的构成
  • 2. 二叉树的遍历
    • 2.1 前序遍历
    • 2.2 中序遍历
    • 2.3 后序遍历
  • 3. 获取整棵二叉树的节点个数
  • 4. 获取二叉树叶子节点的个数
  • 5. 获取第K层节点的个数
  • 6. 获取二叉树的高度(深度)
  • 7. 在二叉树中寻找目标值
  • 8. 判断二叉树是不是完全二叉树
  • 9. 层序遍历

1. 二叉树结点的构成

这里采用的是孩子表示法, 所以节点类(使用的是静态内部类)中除了数值域外要有两个引用来表示节点的左子树和右子树.

static class TreeNode {
        public char val;//数值
        public TreeNode left;//左子树引用
        public TreeNode right;//右子树引用

        public TreeNode(char val) {
            this.val = val;
        }
    }

2. 二叉树的遍历

二叉树的遍历 (Traversal) 是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题(比如:打印节点内容、节点内容加 1)。 遍历是二叉树上最重要的操作之一,是二叉树上进行其它运算之基础。

其实不管是前序遍历,中序遍历,还是后续遍历,二叉树的遍历所走的路径都是相同的,三者之间的区别只是获取根节点数据的时机不同。

2.1 前序遍历

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点—>根的左子树—>根的右子树。

我们利用递归解决问题的思想, 可以将一个问题拆解为子问题去解决, 也就是实现下面的过程:

  • 访问根节点数据;
  • 前序遍历左子树;
  • 前序遍历右子树;

递归结束条件:如果结点root为空,则返回。

//前序遍历
    public void preOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        System.out.print(root.val+" ");
        preOrder(root.left);
        preOrder(root.right);
    }

2.2 中序遍历

中序遍历(Inorder Traversal)——根的左子树—>根节点—>根的右子树;

和上面的实现思想相同, 只是访问根节点的时机不同;

  • 中序遍历左子树;
  • 访问根节点数据;
  • 中序遍历右子树;

递归结束条件:如果结点root为空,则返回。

//中序遍历
    public void InOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        InOrder(root.left);
        System.out.print(root.val+" ");
        InOrder(root.right);
    }

2.3 后序遍历

同样的, 实现过程如下,

  • 后序遍历左子树;
  • 后序遍历右子树;
  • 访问根结点数据;

递归结束条件:如果结点root为空,则返回。

//后序遍历
    public void postOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val+" ");
    }

3. 获取整棵二叉树的节点个数

获取树中的节点个数, 最容易想到的就是遍历一遍树, 通过计数实现了, 代码写起来也不难;

也可以通过递归解决子问题的思想来实现 , 本质上还是在遍历二叉树

节点的个数等于根节点(1) + 左子树节点个数 + 右子树节点个数 ,

递归结束条件: 如果结点root为空,则返回。

    //获取树中节点的个数,遍历计数法
    public static int nodeSize;
    public int size(TreeNode root) {
        //先将nodeSzie置为0
        nodeSize = 0;
        sizefunc(root);
        return nodeSize;
    }
    public void sizefunc(TreeNode root) {
        if(root == null) {
            return;
        }
        nodeSize++;
        sizefunc(root.left);
        sizefunc(root.right);
    }

    //获取树中节点的个数,子问题思想
    public int size2(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return size2(root.left) + size2(root.right) + 1;
    }

4. 获取二叉树叶子节点的个数

同样的思考的话和上面一样, 可以采用计数和子问题来实现, 不过本质上是差不多的;

递归思路:

  • 如果结点为空,表示该树没有结点返回0,
  • 如果结点的左右子树都为空,表示该结点为叶子结点,计算器+1或者返回1。
  • 一棵二叉树的叶子结点数为左右子树叶子结点数之和。
    //获取叶子节点的个数,子问题思想
    public int getLeafNodeCount(TreeNode root){
        if(root == null) {
            return 0;
        }
        if(root.left == null && root.right == null) {
            return 1;
        }
        return getLeafNodeCount(root.left) + getLeafNodeCount(root.right);
    }
    //获取叶子节点的个数,遍历计数法
    public static int leafSize;
    public int getLeafNodeCount2(TreeNode root){
        leafSize = 0;
        getLeafNodeCount2func(root);
        return leafSize;
    }
    public void getLeafNodeCount2func(TreeNode root) {
        if(root == null) {
            return;
        }
        if(root.left == null && root.right == null) {
            leafSize++;
        }
        getLeafNodeCount2func(root.left);
        getLeafNodeCount2func(root.right);
    }

5. 获取第K层节点的个数

递归思路:

  • 如果结点为空,返回0,k为1,返回1。
  • 一棵二叉树第k层结点数为 左子树和右子树第k-1层次的结点数之和。

当k=1时,表示第一层次的结点个数,结点个数为1,每递归一层,从根节点来说是第k层, 那么相对于根节点的子树来说就是k-1层,所以一棵二叉树第k层结点数为左子树,右子树第k-1层次的结点数之和。

public int getKLevelNodeCount(TreeNode root, int k) {
        if(root == null || k <= 0) {
            return 0;
        }
        if(k == 1) {
            return 1;
        }
        return getKLevelNodeCount(root.left, k-1) + getKLevelNodeCount(root.right, k-1);
    }

6. 获取二叉树的高度(深度)

递归思路:

如果根结点为空,则这棵树的高度为0,返回0。

一棵二叉树的最深深度即为左右子树深度的最大值加上1。

// 获取二叉树的高度
    public int getHeight(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftHight = getHeight(root.left);
        int rightHight = getHeight(root.right);
        return leftHight>rightHight ? leftHight+1 : rightHight+1;
    }

7. 在二叉树中寻找目标值

通过遍历去搜索比较即可, 前中后序遍历都可以.

//检测值为val的元素是否存在
    public boolean find(TreeNode root, char val) {
        if(root == null) {
            return false;
        }
        if(root.val == val) {
            return true;
        }
        boolean ret1 = find(root.left, val);
        if(ret1){
            return true;
        }
        boolean ret2 = find(root.right, val);
        if(ret2){
            return true;
        }
        return false;
    }

8. 判断二叉树是不是完全二叉树

判断一棵树是不是完全二叉树,我们可以设计一个队列来实现,

完全二叉树按照从上至下, 从左到右的顺序节点之间是连续着没有空位置的, 这里如果有不了解的可以看一看二叉树概念篇的博客; 如果一颗二叉树不是完全二叉树 , 那么树中的节点之间是有空着的位置的(null); 只要找到这个位置, 后面再没有节点了就是完全二叉树; 如果空位置后面还有节点就不是完全二叉树;

我们可以设计一个队列来实现, 首先将根节点入队,然后循环每次将队头元素出队同时将出队节点的左右孩子结点(包括空结点)依次入队,以此类推,直到获取的结点为空(就是上面说的空位置),此时判断队列中的所有元素是否为空,如果为空,就表示这棵二叉树为完全二叉树 ; 否则就不是完全二叉树.

//判断一棵树是不是完全二叉树
    public boolean isCompleteTree(TreeNode root) {
        if(root == null) {
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            if(cur == null) {
                break;
            }
            queue.offer(cur.left);
            queue.offer(cur.right);
        }
        //判断队列中是否有不为空的元素
        int size = queue.size();
        while(size != 0) {
            size--;
            if(queue.poll() != null) {
                return false;
            }
        }
        return true;
    }

9. 层序遍历

层序遍历的实现方式与判断一棵二叉树是否是完全二叉树类似,都是使用队列来进行实现,只有一点不同, 入队时如果结点为空,则不需要入队,其他的地方完全相同, 出队时获取到节点中的元素, 直到最终队列和当前结点均为空时,表示遍历结束。

//层序遍历
    public void levelOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            System.out.print(cur.val+" ");
            if(cur.left != null) {
                queue.offer(cur.left);
            }
            if(cur.right != null) {
                queue.offer(cur.right);
            }
        }
     }

到此这篇关于Java实现二叉树的基本操作详解的文章就介绍到这了,更多相关Java二叉树内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 数据结构进阶二叉树题集下

    目录 1.对称二叉树 2.创建并遍历二叉树 3.二叉树中两节点最近公共祖先 4.二叉搜索树与双向链表 5.根据前序和中序遍历结果创建二叉树 6.二叉树创建字符串 7.非递归实现二叉树前序遍历 8.非递归实现二叉树后序遍历 1.对称二叉树 [OJ链接] 分为以下几种情况: 二叉树为空,是对称二叉树 二叉树不为空,其左子树或者右子树为空,不是对称二叉树 二叉树不为空,左右子树都为空,是对称二叉树 二叉树不为空,左右子树不为空,左右子节点值不同,不是对称二叉树 二叉树不为空,左右子树不为空,左右子节点

  • Java二叉树查询原理深入分析讲解

    目录 二叉查询树 结点实现原理 插入实现原理 遍历实现原理 删除实现原理 结点插入与遍历案例 二叉查询树 概述 二叉树(Binary tree)是树形结构的一个重要类型.许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要.二叉树特点是每个节点最多只能有两棵子树,且有左右之分 特点 树同时具有数组查询的效率.链表增删.改的性能 右子树的结点比左子树的节点大 查找法 搜索的数字如果比节点大则往右搜索,搜

  • Java数据结构之树和二叉树的相关资料

    目录 什么是树? 简单认识树 树的概念 树的表示形式 二叉树 二叉树的概念 特殊的二叉树 二叉树的性质 二叉树性质相关习题 实现二叉树的基本操作 了解二叉树的存储结构 简单构造一棵二叉树 二叉树的前序遍历 二叉树的中序 获取二叉树节点的个数 获取二叉树叶子节点个数 获取第k层的节点个数 获取二叉树的高度 检测值为value的元素是否存在 层序遍历 判断一棵二叉树是否为完全二叉树 什么是树? 简单认识树 在生活中,有杨树,石榴树,枣树,而在计算机中的树呢,是一种非线性结构,是由 n(n>=0) 个

  • Java 数据结构进阶二叉树题集上

    目录 1.二叉树的遍历 (1)前.中.后序遍历 (2)层序遍历 2.获取树中子结点的个数 3.获取二叉树的高度 4.判断是不是完全二叉树 5.判断两个树是否相同 6.另一棵树的子树 7.判断平衡二叉树 二叉树操作的代码大多数使用递归来实现,代码会比较简洁,如果使用非递归,代码会比较的繁荣,而且不易理解.(上)中的题偏向于基础,后面(下)中的题机会比较难. 1.二叉树的遍历 (1)前.中.后序遍历 这里写到的遍历是递归遍历,代码比较简单,后续会写非递归的代码.以前序遍历为例: 如果根节点root为

  • Java OpenCV学习之Mat的基本操作详解

    目录 使用OpenCV时你需要补充的知识 Mat对象 Mat划线 Mat在己有图片上加圆圈 ImageShowAddCircle.java ImageViewer.java Mat与Image互转 OpenCVUtil.java Mat使用blur图片 环境好了,我们就可以进入正文了. 在之前入门一.二中分别已经有画图的两个例子了.但没有细节展开我们的代码和OpenCV到底在干什么. 使用OpenCV时你需要补充的知识 你需要熟练使用Java Swing,或者是其它任何一门语言中关于GUI方面的

  • java TreeMap源码解析详解

    java TreeMap源码解析详解 在介绍TreeMap之前,我们来了解一种数据结构:排序二叉树.相信学过数据结构的同学知道,这种结构的数据存储形式在查找的时候效率非常高. 如图所示,这种数据结构是以二叉树为基础的,所有的左孩子的value值都是小于根结点的value值的,所有右孩子的value值都是大于根结点的.这样做的好处在于:如果需要按照键值查找数据元素,只要比较当前结点的value值即可(小于当前结点value值的,往左走,否则往右走),这种方式,每次可以减少一半的操作,所以效率比较高

  • java实现队列数据结构代码详解

    什么是队列结构 一种线性结构,具有特殊的运算法则[只能在一端(队头)删除,在另一端(队尾)插入]. 分类: 顺序队列结构 链式队列结构 基本操作: 入队列 出队列 给出一些应用队列的场景 1):当作业被送到打印机的时候,就可以按到达的顺序排起来,因此每一份作业是队列的节点. 2):售票口的人买票的顺序的按照先来先买的顺序售票. 3):当所有的终端被占用,由于资源有限,来访请求需要放在一个队列中等候. 队列是先进先出的! 我们设置一个叫做LinkQueue<T>的泛型集合类,该类里面有 Node

  • 一个通用的Java分页基类代码详解

    分页的基类 import java.util.List; /** * 分页显示的标准类,基本操作,是先给予-当前页数一共的数据条数-每页显示的条数, * 然后在初始化该类,得到总共页数,和开始序号和结束序号, * 然后数据库分页用到开始序号和结束序号,得到数据集合后赋值给该类的list属性, * * 然后把该类发送到jsp页面,进行访问 * @author admin * * @param <T> */ public class PageBean<T> { private int

  • Java使用Redis及其优化详解

    目录 前言 开启远程连接 Jedis连接Redis 封装Jedis进行操作 前言 所有坚韧不拔的努力迟早会取得报酬的.-- 安格尔 开启远程连接 Redis默认是不支持远程连接的,这里需要手动开启远程连接. 关闭本机IP绑定,允许远程连接.找到redis.conf中的bind:127.0.0.1将其注释. 开启密码校验.找到redis.conf中的requirepass去掉其注释并设置密码. Jedis连接Redis 创建一个Maven项目,导入Jedis依赖. <dependency> &l

  • Java数据结构之散列表详解

    目录 介绍 1 散列表概述 1.1 散列表概述 1.2 散列冲突(hash collision) 2 散列函数的选择 2.1 散列函数的要求 2.2 散列函数构造方法 3 散列冲突的解决 3.1 分离链接法 3.2 开放定址法 3.3 再散列法 4 散列表的简单实现 4.1 测试 介绍 本文详细介绍了散列表的概念.散列函数的选择.散列冲突的解决办法,并且最后提供了一种散列表的Java代码实现. 数组的特点是寻址容易,插入和删除困难:而链表的特点是寻址困难,插入和删除容易.而对于tree结构,它们

  • Java数据结构之线段树详解

    目录 介绍 代码实现 线段树构建 区间查询 更新 总结 介绍 线段树(又名区间树)也是一种二叉树,每个节点的值等于左右孩子节点值的和,线段树示例图如下 以求和为例,根节点表示区间0-5的和,左孩子表示区间0-2的和,右孩子表示区间3-5的和,依次类推. 代码实现 /** * 使用数组实现线段树 */ public class SegmentTree<E> { private Node[] data; private int size; private Merger<E> merge

  • Java数据结构之堆(优先队列)详解

    目录 堆的性质 堆的分类 堆的向下调整 堆的建立 堆得向上调整 堆的常用操作 入队列 出队列 获取队首元素 TopK 问题 例子 数组排序 堆的性质 堆逻辑上是一棵完全二叉树,堆物理上是保存在数组中 . 总结:一颗完全二叉树以层序遍历方式放入数组中存储,这种方式的主要用法就是堆的表示. 并且 如果已知父亲(parent) 的下标, 则: 左孩子(left) 下标 = 2 * parent + 1; 右孩子(right) 下标 = 2 * parent + 2; 已知孩子(不区分左右)(child

  • Java VisualVM监控远程JVM(详解)

    我们经常需要对我们的开发的软件做各种测试, 软件对系统资源的使用情况更是不可少, 目前有多个监控工具, 相比JProfiler对系统资源尤其是内存的消耗是非常庞大,JDK1.6开始自带的VisualVM就是不错的监控工具. 这个工具就在JAVA_HOME\bin\目录下的jvisualvm.exe, 双击这个文件就能看到一个比较直观的界面 从左边Applications树中可以知道,不光可以监控本地JVM运行情况, 还可以监控远程机器上的JVM运行情况. 本地监控:只要打开某个JAVA程序就会自

  • 基于java servlet过滤器和监听器(详解)

    1 过滤器 1.过滤器是什么? servlet规范当中定义的一种特殊的组件,用于拦截容器的调用. 注:容器收到请求之后,如果有过滤器,会先调用过滤器,然后在调用servlet. 2.如何写一个过滤器? 1.写一个java类,实现Filter接口; 2.在接口方法中实现拦截方法; 3.配置过滤器(web.xml); 3.配置初始化参数 1.配置初始化参数.(init-param) 2.通过filterconfig提供的getinitparamenter方法读取初始化的值. 4.优先级: 当有多个过

随机推荐