首页 > 编程语言 >Java-数据结构-二叉树-基础 (o゚▽゚)o

Java-数据结构-二叉树-基础 (o゚▽゚)o

时间:2024-09-12 12:51:05浏览次数:13  
标签:结点 null TreeNode return 二叉树 Java 数据结构 root

文本目录:

❄️一、树形结构:

   ▶ 1、概念:

▶ 2、特殊的概念:

 ▶ 3、树的表示形式:

❄️二、二叉树:

   ▶ 1、概念:

   ▶ 2、两种特殊的二叉树:

          ➷ 1)、满二叉树:

           ➷ 2)、完全二叉树:

  ▶ 3、二叉树的性质:

       ➷ 1)性质一:

        ➷ 2)性质二:

         ➷ 3)性质三:

 ​编辑

         ➷ 4)性质四:

          ➷ 5)性质五:

   ▶ 4、二叉树的存储:

    ▶ 5、二叉树的基本操作:

        ➷ 1)、二叉树的创建:

 ➷ 2)、二叉树的遍历:

           •(1)、前中后序遍历:

             •(2)、层序遍历:

  ➷ 3)、二叉树的基本操作:

            •(1)、size(Node root):

             •(2)、getLeafNodeCount(Node root):

              •(3)、getKLevelNodeCount(Node root,int k):

               •(4)、getHeight(Node root):

                •(5)、find(Node root,char val):

                 •(6)、isCompleteTree(Node root):

❄️总结:


❄️一、树形结构:

     在我们介绍二叉树之前呢,我们先来了解一下什么是树形结构。

   ▶ 1、概念:

       树是一种 非线性的数据结构 ,其是由 n 个有限节点组成的一个具有层次关系的集合。其结构呢就像是一个倒挂着的一颗树,所以我们称之为树。

      特点:1、有一个特殊的节点,称为根节点,并且根节点没有前驱节点。

                 2、除了根节点外,其余节点被分为 M 个互不相交的集合,其中呢每一个集合都是一颗 与树相似的子树,每个子树的根节点有且只有一个前驱,其中可以有0个或者多个后继结点。

                 3、 我们的树是递归实现的。

                 4、一颗有N个节点的树,有N - 1 条边。

这里要注意:树形结构中呢,我们的子树之间不能有交集,否则就不是树形结构了。


▶ 2、特殊的概念:

 我们先来看个树形结构的图片: 


☑ 结点的度:一个结点含有子树的个数称之为该结点的度。比如:上图中 A 的度为 6 。

☑ 树的度:一棵树中,所有 结点的度 的最大值称之为 树的度。比如:我们上图的树的度为 6 

☑ 叶子结点或终端结点:度为 0 的结点称之为叶子结点。比如:我们上图的B、C、H、I、K....等

☑ 双亲结点或父结点:若一个结点含有子结点,则这个节点称之为其子结点的父结点。比如:我                                       们上图的 A 是 B 的父结点。

☑ 孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点。比如:B是A的孩子结                                      点

☑ 根结点:一棵树中没有双亲结点的结点。比如:A结点

☑ 结点的层次:从根结点开始定义起,根为第一层1,根的子结点为第2层,以此类推。

☑ 树的高度或者深度:树中结点的最大层次。比如:上图中树的高度为 4


接下来的概念呢,我们只需要了解即可:

☒ 非终端结点或分支结点:度不为 0 的结点。比如:A、D、E、F、G....等

☒ 兄弟结点:具有相同父结点的结点互相称之为兄弟结点。比如:B、C就是兄弟结点

堂兄弟结点:双亲在同一层的结点互为堂兄弟。比如:H、I 就是堂兄弟

结点的祖先:从根到该结点所经分支上的所有结点。比如:Q 的祖先就是J、E、A

子孙:以结点为根的子树中任意结点都称之为该结点的子孙。比如:所有结点都是A的子孙

森林:由m 棵互不相交的树组成的就是森林


 ▶ 3、树的表示形式:

        树结构呢,相对于线性结构呢就比较复杂了,我们存储表示起来就比较麻烦了。我们对于树有很多的表示形式,比如:双亲表示法、孩子表示法、孩子双亲表示法、孩子兄弟表示法。

        我们来了解一下我们最常用的表示方法:孩子兄弟表示法

class Node {
   int val;
   Node firstChild;//第一个孩子引用
   Node nextBrother;//下一个兄弟引用
}

 

 这个呢就是我们的孩子兄弟表示法。


❄️、二叉树:

   ▶ 1、概念:

       一颗二叉树是结点的一个有限集合,这个集合呢:

1、或者为空

2、或者是由一颗根结点加上两个子树,分别为 左子树 和 右子树 所构成的。

我们来看看二叉树的图片: 

从上面的图片可知:1、二叉树不存在结点大于2的情况。

                                 2、二叉树的子树有左右之分,次序不能颠倒,所以二叉树是有序的。

 注意:我们二叉树可能出现几种情况:


   ▶ 2、两种特殊的二叉树:

          1)、满二叉树:

         一颗二叉树,如果每层的结点数都达到最大值,则这颗二叉树就是满二叉树。

就是 如果一颗二叉树的结点为 k,并且结点总数为 2^k - 1 个结点,则其就是满二叉树


            2)、完全二叉树:

        完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树。

        这样看文字可能不是很看懂,我们来看图片:
完全二叉树的存放顺序为:从上到下,从左到右,依次存放。


  ▶ 3、二叉树的性质:

       ➷ 1)性质一:

       若规定根结点的层数为1,则一颗 非空二叉树 的第 i 层上最多有 2^(i - 1) 个结点。


        ➷ 2)性质二:

     若规定只有根结点的二叉树的深度为1,则深度为 k 的二叉树的最大结点数为 2^k - 1


         ➷ 3)性质三:

对于任意的一颗二叉树,如果其叶子结点个数为n0,度为2的非叶子结点数为n2,则有n0 = n2 + 1

 


         ➷ 4)性质四:

   具有 n 个结点的 完全二叉树 的 深度k 为 log(n+1) 向上取整,这里的 log是以2为底的

 


          ➷ 5)性质五:

        对于具有n个结点的完全二叉树,如果按照 从上至下从左至右 的顺序对所有节点从0开始编号,则对于序号为 i 的结点有:

          ☞  若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点

          ☞  若2i+1<n,左孩子序号:2i+1,否则无左孩子

          ☞  若2i+2<n,右孩子序号:2i+2,否则无右孩子


   ▶ 4、二叉树的存储:

   二叉树的存储解构呢,分为:顺序存储链式存储

我们先来看 链式存储:是通过一个一个结点引用起来的,我们常见的有二叉三叉表示形式。

//二叉
class Node {
    int val;//数据域
    Node left;//左孩子引用
    Node right;//右孩子引用
}

//三叉
class Node {
    int val;//数据域
    Node left;//左孩子引用
    Node right;//右孩子引用
    Node parent;//当前结点的根结点
}

    ▶ 5、二叉树的基本操作:

        ➷ 1)、二叉树的创建:

     我们这里呢只是进行简单的创建,手动的进行创建一个二叉树,之后我们再来写其操作,等到后面我们再来进行二叉树的真正的创建方法。

我们来手动创建这个二叉树,我们使用二叉的方式来进行创建。 

public class BinaryTree {

    static class TreeNode {
        public char val;
        public TreeNode right;
        public TreeNode left;

        public TreeNode(char val) {
            this.val = val;
        }
    }
    public TreeNode createTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');

        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        return A;
    }
}

      这样我们就把这个二叉树创建成功了,注意这里的二叉树创建不是真正的二叉树的创建。我们接下来看看二叉树的操作:


 ➷ 2)、二叉树的遍历:

    对于我们的二叉树操作呢,最简单的就是遍历操作了,所谓的遍历:就是沿着某条搜索路线,依次对树中每个结点均做一次访问且只做一次访问

           •(1)、前中后序遍历:

  我们在前中后序遍历中使用递归的方法进行遍历。

前序遍历:先访问根结点,之后访问根的左子树,最后根的右子树。

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

中序遍历:先访问根的左子树,之后访问根结点,最后为根的右子树。 

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

 后序遍历:先访问根的左子树,之后访问根的右子树,最后访问根结点。

//后序遍历
    public void postOrder(TreeNode root) {
        if (root == null) {
            return;
        }

        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val + " ");
    }

我们来看这些遍历的运行结果,我们遍历的就是我们上面创建的二叉树。


             •(2)、层序遍历:

     我们除了前中后序遍历呢,我们还有层序遍历,就是先访问第一层的根结点,之后从上到下,从左到右依次访问结点,就是层序遍历。

比如我们创建的这个二叉树的层序遍历为:

A->B->C->D->E->F->G。 

这里我们层序遍历,我们需要使用队列来配合实现,那么它是怎么做到的呢?我们来看看:

 

  来看看代码:

//层序遍历
    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);
            }
        }
    }

  ➷ 3)、二叉树的基本操作:

            •(1)、size(Node root):

         获得树中结点的个数。

我们有两种方法,我们第一种就是遍历二叉树,当 root 不为空的时候呢,我们的 usedSize 就++ 

第二种方法就是我们的 左子树结点+右子树结点+1,这个方法来求结点的个数。

我们来看第一种方法的代码:

//返回总结点的个数
    public static int usedSize;//我们用于存放结点个数的
    public int size1(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        usedSize++;
        size1(root.left);//遍历左子树
        size1(root.right);//遍历右子树
        
        return usedSize;
    }

    在我们第一种方法中呢,我们的 usedSize 必须在方法的外面定义,因为要是在方法中的话呢,每次我们递归后,我们之前存放的结点的个数就不会保存了,所以我们要定义在外面。


我们来看看第二种方法:

代码:

public int size2(TreeNode root) {
        if (root == null) {
            return 0;
        }

        return size2(root.left) + size2(root.right) + 1;
    }

             •(2)、getLeafNodeCount(Node root):

 获得叶子结点的个数。

     我们这个同样可以使用两种方法来进行获得叶子结点的个数。第一种:使用 leafSize 来计数,当遇到左子树和右子树都为空的结点就是叶子结点

    第二种:左子树的叶子+右子树的叶子。

我们的这个方法和上面的方法是差不多的,所以这里我们直接看代码。

第一种:

//获得叶子结点的个数
    //第一种
    public static int leafSize;
    public int getLeafNodeCount1(TreeNode root) {
        if (root == null) {
            return 0;
        }

        if (root.left == null && root.right == null) {
            leafSize++;
        }
        
        getLeafNodeCount1(root.left);
        getLeafNodeCount1(root.right);
        
        return leafSize;
    }

第二种: 

//第二种
    public int getLeafNodeCount2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        if (root.left == null && root.right == null) {
            return 1;
        }
        
        return getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);
    }

              •(3)、getKLevelNodeCount(Node root,int k):

获得第k层的结点的个数。

   我们求第k层的结点数呢,我们求  root的左子树的k-1层的结点 + root的右子树的k-1层的结点

这样求出来的就是我们的第k层结点数。我们来看看思路图: 

我们来看代码的实现: 

//获得第k层的结点的个数。
    public int getKLevelNodeCount(TreeNode root,int k) {
        if (root == null) {
            return 0;
        }
        if (k == 1) {
            return 1;
        }
        
        return getKLevelNodeCount(root.left,k-1) + getKLevelNodeCount(root.right,k-1);
    }

               •(4)、getHeight(Node root):

获得 二叉树的高度。

     我们需要求 左树的高度 和 右子树的高度的最大值 + 1。在这里我们求 左子树的高度 和  右子树的高度。我们也是使用递归的方法。

 代码:

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

                •(5)、find(Node root,char val):

查看二叉树中有没有val这个值的结点。

这个方法呢,就是我们需要遍历一遍二叉树就可以了。

    我们需要先判断我们的根是不是,如果不是,就去左子树中找,如果还没有,就从右子树中找,如果这里面都没有的话,那么就返回null。

//检测值为value的元素是否存在
    public TreeNode find(TreeNode root,char val) {
        if (root == null) {
            return null;
        }
        //先判断根结点
        if (root.val == val) {
            return root;
        }
        //如果不是就去左子树中寻找
        TreeNode leftNode = find(root.left,val);
        if (leftNode != null) {
            //如果我们在左子树中找到的话,我们在上面根的判断就返回了我们和val相等的结点,
            //在这里我们只需要判断是否为空就可以了,不为空,就是val的这个结点,反之则没有
            return leftNode;
        }
        //去右子树中寻找
        TreeNode rightNode = find(root.right,val);
        if (rightNode != null) {
            //我们这里同上
            return rightNode;
        }
        
        return null;
    }

                 •(6)、isCompleteTree(Node root):

判断该二叉树是否是完全二叉树。

     这个方法呢,我们也需要借助队列来实现,这个步骤和我们的层序遍历是差不多的,但是如果我们队列中两个结点中存在 null 的话,那么就不是完全二叉树。因为完全二叉树是从上到下从左到右依次存放,两个相邻的结点中间不可能会存在 null 。我们利用这个性质就可以判断出是否是完全二叉树了。我们来看思路图:

这里我们要注意的是,当我们的root为空的时候,也是完全二叉树。

我们来看代码:

//判断一棵树是不是完全二叉树
    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) {
                queue.offer(cur.left);
                queue.offer(cur.right);
            }else {
                break;
            }
        }
        
        while(!queue.isEmpty()) {
            TreeNode peek = queue.peek();
            if (peek != null) {
                return false;
            }
            queue.poll();
        }
        
        return true;
    }

❄️总结:

   OK,到这里呢,我们的二叉树基础就到这里就结束了,我们接下来看看关于二叉树的相关的习题,并且在题中我们会看见我们 二叉树的创建方法。让我们期待下次的博客吧!!!拜拜啦~~~

标签:结点,null,TreeNode,return,二叉树,Java,数据结构,root
From: https://blog.csdn.net/2303_80388948/article/details/142106093

相关文章

  • 5-【JavaWeb】JUnit 单元测试及JUL 日志系统
    1.使用JUnit进行单元测试JUnit是Java中非常流行的单元测试框架,MyBatis与JUnit可以很好地结合,来测试持久层代码的正确性。1.1添加JUnit依赖在使用JUnit之前,需要在pom.xml中引入JUnit依赖。<dependency><groupId>junit</groupId><artifactId>......
  • 6- 【JavaWeb】Maven管理项目
    ApacheMaven是一个流行的构建自动化工具,用于Java项目的构建、管理和依赖处理。Maven使用XML配置文件pom.xml来管理项目的构建过程和依赖关系。1.项目结构一个标准的Maven项目结构如下:my-maven-project/├──src/│├──main/││├──java......
  • 3-【JavaWeb】Lombok配置及使用方法介绍
    Lombok入门教程1.什么是Lombok?Lombok是一个帮助简化Java类中样板代码的Java库。比如,你经常会发现自己重复编写getter和setter方法、构造函数、toString()、equals()和hashCode()方法等。Lombok通过注解来自动生成这些代码,简化开发工作。2.Lombok安装步......
  • java使用多线程
    importjava.util.concurrent.TimeUnit;importcn.hutool.core.thread.ExecutorBuilder;importcn.hutool.core.thread.ThreadFactoryBuilder;//构造多线程,可修改线程数ExecutorServiceexecutorService=ExecutorBuilder.create().setCorePoolSize(5)//初始线程......
  • 基于 Bootstrap+Echarts +Java SpringBoot 实现数字化水资源监测全景驾驶舱项目
    基于Bootstrap+Echarts+JavaSpringBoot实现数字化水资源监测全景驾驶舱项目,此项目前端采用Bootstrap前端框架,结合javaScrip和echarts以及ajax实现前端页面的展现与后端数据进行交互。后端采用JavaSpringBoot开发后台功能,对数据库增加增删改查等操作,给前端提供数据接口。......
  • JAVA的安装与配置
    1.下载Java安装包https://www.oracle.com/java/technologies/javase/javas e8-archive-downloads.html(网址链接) 下载自己需要的版本2.安装JDK找到下载位置【打开】 下一步有需要更改位置下一步到这一步就算下载完成3.配置环境变量  1.在电脑下方搜索......
  • JAVA线程基础——ThreadLocal的使用和原理
    一、ThreadLocal        多线程访问同一个共享变量时特别容易出现并发问题,特别是在多个线程需要对一个共享变量进行写入时。为了保证线程安全,一般使用者在访问共享变量时需要进行适当的同步,如图1-3所示。        同步的措施一般是加锁,这就需要使用者对锁有......
  • Java学习路线:详细指引
    Java学习路线可以分为几个阶段,每个阶段都有其重点和推荐学习的内容。下面我将按照初学者、进阶和高级三个阶段来举例说明:初学者阶段目标:熟悉Java基础语法理解面向对象编程掌握基本数据类型和数据结构学会使用IDE(如IntelliJIDEA或Eclipse)学习内容:Java基础语法:包括变量、......
  • JAVA基础:抽象类,接口,instanceof,类关系,克隆
    1JDK中的包JDK=JRE+开发工具集(javac.exe)JRE=JVM+java类库JVM=java虚拟机jdk中自带了许多的包(类),常用的有java.lang该包中的类,不需要引用,可以直接使用。例如:Object,System,Stringjava.utiljava.sqljava.netjava.iojava.text2抽象方法......
  • 【开源免费】基于SpringBoot+Vue.JS在线视频教育平台(JAVA毕业设计)
    本文项目编号T027,文末自助获取源码\color{red}{T027,文末自助获取源码}......