Java实现二叉树

news2024/9/27 9:22:37

一、树

 1、树简介

树是一种非线性的数据结构,具有n个结点其数据存储形式像一棵倒挂的树,树有一个根结点没有前驱结点,树有多个叶子结点没有后继结点,树有多个中间结点既有前驱结点又有后继结点。

树结构中子树之间不能有交集。

n个结点的树有n-1条边。

像电脑中的文件系统就是树结构存储,树结构如下图所示:

2、树的相关概念 

结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为2

树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为3

叶子结点或终端结点:度为0的结点称为叶结点; 如上图:D、H、I、F、G节点为叶结点

双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:AB的父结点

孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:BA的孩子结点

根结点:一棵树中,没有双亲结点的结点;如上图:A

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

树的高度或深度:树中结点的最大层次; 如上图:树的高度为

非终端结点或分支结点:度不为0的结点; 如上图:D、H、I、F、G节点为分支结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:BC是兄弟结点

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:F、G互为堂兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

森林:由mm>=0)棵互不相交的树组成的集合称为森林,如下图所示:

二、二叉树 

1、二叉树简介

二叉树也是一种特殊的树结构,但是二叉树的度最大只能为2,也就是说,二叉树中只存在度为1的结点和度为2的结点以及叶子结点。

二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。

2、两种特殊的二叉树 

a、满二叉树

满二叉树是每层结点都达到最大值,也就是只存在度为2和度为0的结点。

b、完全二叉树 

完全二叉树是与满二叉树编号为0~n-1的结点一一对应时则称为完全二叉树,满二叉树也是一种特殊的完全二叉树。

3、二叉树的性质 

  • 若规定根的层数为1,则非空二叉树的第k层最多有(2^(k-1))个结点。
  • 若规定根所在的高度为1,则高度为k的二叉树最多有((2^k)-1)个结点。
  • 对于任何一棵二叉树,如果叶子结点数为n,则度为2的结点数为n-1。推导过程如下:

  • 具有n个结点的完全二叉树的深度k为log2(n+1) 上取整。
  • 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i 的结点有:
  1. 若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
  2. 若2i+1<n,左孩子序号:2i+1,否则无左孩子
  3. 若2i+2<n,右孩子序号:2i+2,否则无右孩子

4、二叉树的模拟实现 

采用孩子表示法来存存储结点,结点包含数据域、左孩子以及右孩子。

public static class TreeNode{
        char val;
        TreeNode left;
        TreeNode right;
        public TreeNode(char val){
            this.val=val;
        }
    }

 a、创建一棵二叉树

使用逐个插入元素的方法进行创建。

例如创建如下图所示的二叉树:

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');
        A.left=B;
        A.right=C;
        B.left=D;
        B.right=E;
        C.left=F;
        root=A;
        return root;
    }

b、前序遍历

先序遍历的顺序是:根-左-右,采用递归的方法,先对根进行判断,如果不为空则进行打印,然后递归调用左子树以及右子树。 

public void preOrder(TreeNode root) {
        if(root==null){
            return;
        }
        System.out.println(root.val);
        preOrder(root.left);
        preOrder(root.right);
    }

c、中序遍历 

中序遍历的顺序是左-根-右,同样也是递归的方法,只不过是先递归左子树再打印根结点最后递归右子树。

void inOrder(TreeNode root) {
        if(root==null){
            return;
        }
        inOrder(root.left);
        System.out.println(root.val);
        inOrder(root.right);
    }

d、后序遍历 

后序遍历的顺序是左-右-根,代码实现与中序遍历类似。

void postOrder(TreeNode root) {
        if(root==null){
            return;
        }
        inOrder(root.left);
        inOrder(root.right);
        System.out.println(root.val);
    }

e、层序遍历 

层序遍历是一层一层地进行遍历,需要用到先进先出的队列,如果根结点非空,就将根结点存入队列中,然后只要队列不为空就出队,同时若出队元素的左孩子不为空,就将左孩子加入到队列中,如果右孩子不为空,就将右孩子加入到队列中。

public void levelOrder(TreeNode root) {
        if(root==null){
            return;
        }
        Queue<TreeNode> que=new LinkedList<>();
        que.offer(root);
        while(!que.isEmpty()){
            TreeNode cur=que.peek();
            System.out.println(que.poll().val);
            if(cur.left!=null){
                que.offer(cur.left);
            }
            if(cur.right!=null){
                que.offer(cur.right);
            }
        }
    }

f、获取结点的个数 

首先判断根结点是否为空,若不为空就利用递归或遍历的思想分别获取到左孩子和右孩子的结点数,然后再加1表示加上根结点。

 /**
     * 获取树中节点的个数:遍历思路
     */
    void size(TreeNode root) {
        if(root==null){
            return;
        }
        nodeSize++;
        size(root.left);
        size(root.right);
    }

    /**
     * 获取节点的个数:子问题的思路
     *
     * @param root
     * @return
     */
    int size2(TreeNode root) {
        if(root==null){
            return 0;
        }
        return size2(root.left)+size2(root.right)+1;

    }

d、获取叶子结点的个数 

与上题解法相似,但是需要新增一个递归入口,如果根结点的左孩子和右孩子都为空则返回1,递归或遍历求左孩子与右孩子的叶子结点数。

/*
     获取叶子节点的个数:遍历思路
     */
    public static int leafSize = 0;

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

    /*
     获取叶子节点的个数:子问题
     */
    int getLeafNodeCount2(TreeNode root) {
        if(root==null){
            return 0;
        }
        if (root.left==null&&root.right==null){
            return 1;
        }
        int len1=getLeafNodeCount2(root.right);
        int len2=getLeafNodeCount2(root.left);
        return len1+len2;
    }

f、获取第k层结点的个数

如果一棵树非空,要求第k层的几点的个数,就可以理解为求其左子树和右子树的第k-1层结点之和,如果k为1,表示求第一层则只有根结点,结点个数为1,递归求解。

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

g、获取树的高度 

若树非空,则进行递归求出左子树的高度和右子树的高度并求出两者的较大值还要加1表示根结点的所在层即为求出树的高度。

int getHeight(TreeNode root) {
        if(root==null){
            return 0;
        }
        int height1=getHeight(root.left);
        int height2=getHeight(root.right);
        return height1>height2?height1+1:height2+1;
    }

 h、判断是否为完全二叉树

依据层序遍历的思想,若树非空,则将根结点先入队,然后进行while循环,若出队列的元素不为空就将其左孩子结点和右孩子结点加入队列否则就跳出循环,对队列进行二次遍历队列中存在不为空的元素则就不是完全二叉树否则就是完全二叉树。

boolean isCompleteTree(TreeNode root) {
        if(root==null){
            return false;
        }
        Queue<TreeNode> que=new LinkedList<>();
        que.offer(root);
        while(!que.isEmpty()) {
            TreeNode cur = que.peek();
            if(cur!=null){
                que.offer(cur.left);
                que.offer(cur.right);
            }else{
                break;
            }
            que.poll();
        }
        while (!que.isEmpty()){
            TreeNode node=que.poll();
            if(node==null){
                return false;
            }
        }

        return true;
    }

i、判断是否为平衡树 

平衡树就是树中每个结点的左孩子和右孩子的高度差不能大于1。那么就可以利用递归求每个结点的左右子树高度差,若存在大于1的则不为平衡树,否则为平衡树。

public int getHeight(TreeNode root) {
        if(root==null){
            return 0;
        }
        int height1=getHeight(root.left);
        int height2=getHeight(root.right);
        return height1>height2?height1+1:height2+1;
    }
   public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        int len=Math.abs(getHeight(root.left)-getHeight(root.right));
        return len<=1&&isBalanced(root.left)&&isBalanced(root.right);
    }

上述方法的时间复杂度为O(n^2),那么要想时间复杂度为O(n),则应该怎么做?

那么就可以在求结点的左右高度时进行判断,如果左右高度差大于1就返回-1,表示不平衡,否则就返回左右高度的最大值加1,递归处理,在判断是否为平衡树的函数里只需调用求高度的函数判断其返回值是否为-1。

boolean isBalanceTree2(TreeNode root) {
        return getLen(root)>0;
    }
    int getLen(TreeNode root){
        if(root==null){
            return 0;
        }
        int len1=getLen(root.left);
        if(len1<0) return -1;
        int len2=getLen(root.right);
        if(len2<0) return -1;
        if(Math.abs(len1-len2)<=1){
            return Math.max(len1,len2)+1;
        }else{
            return -1;
        }
    }

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

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

相关文章

【Node.js实战】一文带你开发博客项目之联调(导入HTML、Nginx反向代理、CORS解决跨域、与前端联调)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

Tapdata Cloud 场景通关系列: Oracle → MySQL 异构实时同步

【前言】作为中国的 “Fivetran/Airbyte”, Tapdata Cloud 自去年发布云版公测以来&#xff0c;吸引了近万名用户的注册使用。应社区用户上生产系统的要求&#xff0c;Tapdata Cloud 3.0 将正式推出商业版服务&#xff0c;提供对生产系统的 SLA 支撑。Tapdata 目前专注在实时数…

二叉树的遍历(非递归)

二叉树的遍历 遍历二叉树, 是指按一定的规则和顺序访问二叉树的所有结点, 使每一个结点都被访问一次, 而且只被访问一次. 由于二叉树是非线性结构, 因此, 二叉树的遍历实质上是将二叉树的各个结点排列成一个线性序列. DFS: 前序, 中序及后序. BFS: 是指沿着二叉树的宽度优先遍…

Leetcode.1806 还原排列的最少操作步数

题目链接 Leetcode.1806 还原排列的最少操作步数 题目描述 给你一个偶数 ​n​n​n​​​​​ &#xff0c;已知存在一个长度为 nnn 的排列 permpermperm &#xff0c;其中 perm[i]iperm[i] iperm[i]i​&#xff08;下标 从 0 开始 计数&#xff09;。 一步操作中&#xff0…

OLAP和OLTP的区别

OLAP和OLTP的区别 OLAP&#xff1a; (Online transaction processing):在线/联机事务处理。典型的OLTP类操作都比较简单&#xff0c;主要是对数据库中的数据进行增删改查&#xff0c;操作主体一般是产品的用户。 OLTP&#xff1a; (Online analytical processing):指联机分…

Vue新一代状态管理工具—Pinia—都2023年了,快学起来吧!

Pinia 基本介绍 Pinia 是 Vue.js 的轻量级状态管理库 官方网站&#xff1a;https://pinia.vuejs.org/ 中文文档: https://pinia.web3doc.top/introduction.html 为什么学习pinia? pinia和vuex4一样&#xff0c;也是vue 官方 状态管理工具(作者是 Vue 核心团队成员&#xff…

基于JAVA SSM框架的影院管理系统源码,实现包括影院管理,电影管理,影厅管理,排片管理,选座售票,演员管理,影片评论等功能

介绍 下载地址&#xff1a;基于JAVA SSM框架的影院管理系统源码 该项目是一个电影信息管理、发布、展示平台&#xff0c;终端用户可以浏览、购票、评论。项目主要实现包括影院管理&#xff0c;电影管理&#xff0c;影厅管理&#xff0c;排片管理&#xff0c;选座售票&#xff…

连号区间数(第四届蓝桥杯省赛C++B组,第四届蓝桥杯省赛JAVAB组)

题目详细&#xff1a;解题思路&#xff1a;对于这个题目如果一开始没有思路的话我们可以先想一下暴力写法暴力的话就是不断的枚举每个区间然后判断这个区间是否合法这样写下来用了三重循环而对于题目我们只能通过部分样例所以我们就要想办法取缩减它的时间对于遍历每个区间我们…

【SpringBoot】使用AOP+注解实现请求参数的指定自动填充

首先定义一个加在方法上的注解 import java.lang.annotation.*;/*** 开启自动参数填充*/ Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD}) Documented Inherited public interface AutoParameterFill {/*** 要填充的字段名,不写的话默认下面类的子类中的字段…

Redis未授权访问漏洞(一)先导篇

前言 Redis默认情况下&#xff0c;会绑定在0.0.0.0:6379&#xff0c;如果没有进行采用相关的策略&#xff0c;比如添加防火墙规则避免其他非信任来源ip访问等&#xff0c;这样将会将Redis服务暴露到公网上。 如果在没有设置密码认证&#xff08;一般为空&#xff09;的情况下…

InceptionNet与ResNet

以下代码图片思路来源&#xff1a; 北京大学Tensor flow笔记 嗯,最近学了一下神经网络&#xff0c;并没有很难&#xff0c;主要是把代码背下来&#xff0c;然后掌握Tensorflow是怎么搭建网络的&#xff0c;Tensorflow是比pytorch好用的&#xff0c;我直接抄的代码里面&#xff…

UDS诊断系列介绍05-27服务

本文框架1. 系列介绍27服务概述2. 27服务请求与应答2.1 27服务请求2.2 27服务肯定应答2.3 27服务否定应答1. 系列介绍 UDS&#xff08;Unified Diagnostic Services&#xff09;协议&#xff0c;即统一的诊断服务&#xff0c;是面向整车所有ECU的一种诊断通信方式&#xff0c;…

java-操作excel

文章目录java操作Excel数据使用场景excel 03 和 07的区别POIeasyExcel解析excel表中的对象POI使用步骤POI 写数据POI 读数据计算公式easyExcel读写数据写数据读数据java操作Excel数据 在 平时 可以使用IO流对Excle进行操作 但是现在使用更加方便的第三方组件来实现 使用场景 1、…

在rhel7系统使用Mariadb

文章目录一 联系和区别二 需求三 部署安装3.1 环境准备3.2 安装软件包3.3 启动服务3.4 设置防火墙策略四 创建用户和库表4.1 登录数据库4.2 创建用户4.3 创建数据库和表五 备份和恢复5.1 备份 com 数据库5.2 模拟误删除操作5.3 恢复表一 联系和区别 Mariadb是由社区开发的一个…

4.4 集成运放的性能指标及低频等效电路

一、集成运放的性能指标 在考察集成运放的性能时&#xff0c;常用下列参数来描述&#xff1a; 1、开环差模增益 AodA_{od}Aod​ 在集成运放无外加反馈时的差模放大倍数称为差模开环增益&#xff0c;记作 AodA_{od}Aod​。AodΔuO/(uP−uN)A_{od}\Delta u_O/(u_P-u_N)Aod​Δ…

【Spring Cloud GateWay】ServerHttpResponseDecorator不生效

文章目录1. BUG描述2. BUG解决3. BUG分析1. BUG描述 在Spring Cloud Gateway使用编码的方式实现一个全局拦截器&#xff0c;在全局拦截器中想要打印响应日志。 于是自己装饰了一个具有打印日志功能的ServerHttpResponseDecorator&#xff0c;但是在转发后的服务返回响应的时候…

在线浏览PDF:Grapecity Documents for PDF Viewer 6.0.2

Grapecity Documents for PDF Viewer跨平台 JavaScript PDF 查看器---备注:必须配合.NET版本才能编辑PDF 使用我们的 JavaScript PDF 查看器在网络上阅读和编辑 PDF。跨浏览器和框架打开和打印。 Grapecity Documents for PDF Viewer全功能的 JavaScript PDF 查看器和 PDF 编辑…

move语言之基础学习(基本类型+表达式+变量)例子

一、基本类型 Move 的基本数据类型包括: 整型 (u8, u64, u128)、布尔型 boolean 和地址 address。 Move 不支持字符串和浮点数。 整型 整型包括 u8、u64 和 u128&#xff0c;我们通过下面的例子来理解整型&#xff1a; script { fun main() { // define empty variable, set…

python(0)计算机基础知识

文章目录计算机是什么计算机的组成计算机的使用方式windows的命令行文本文件和字符集乱码计算机是什么 在现实生活中&#xff0c;越来越无法离开计算机了 电脑、笔记本、手机、游戏机、汽车导航、智能电视。。。 计算机就是一个用来计算的机器 目前来讲&#xff0c;计算机只…

C++模板进阶+继承详解

耕耘和收获不是连贯的&#xff0c;中间还隔着很长一段时间&#xff0c;那就是坚持&#xff01;一&#xff1a;模板进阶1.1&#xff1a;非类型模板参数template<class T,size_t N> class arr { private:T _a[N]; };这里的N就跟define一样&#xff0c;属于非类型模板参数。…