二叉树的实现

news2024/11/23 15:07:23

二叉树

文章目录

  • 二叉树
    • 背景
    • 二叉树的概念
    • 遍历方式
    • 代码实现

背景

数组存储方式的分析

优点:通过下标方式访问元素,速度快。对于有序数组,还可使用二分查找提高检索速度。

缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较低

链式存储方式的分析

优点:在一定程度上对数组存储方式有优化(比如:插入一个数值节点,只需要将插入节点,链接到链表中即可, 删除效率也很好)。

缺点:在进行检索时,效率仍然较低,比如(检索某个值,需要从头节点开始遍历)

树存储方式的分析

能提高数据存储,读取的效率, 比如利用 二叉排序树(Binary Sort Tree),既可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度。

知道数的优点那么就得知道树的常用术语有哪些

在这里插入图片描述

二叉树的概念

1.树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树。

2.二叉树的子节点分为左节点和右节点。

3.如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1 , n 为层数,则我们称为满二叉树。

4.如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。

例如下图:

在这里插入图片描述

遍历方式

二叉树有三种遍历方式

分别为前序遍历,中序遍历,后序遍历

前序遍历:先输出父节点,再遍历左子树和右子树

中序遍历:先遍历左子树,再输出父节点,再遍历右子树

后序遍历:先遍历左子树,再遍历右子树,最后输出父节点

小结: 看输出父节点的顺序,就确定是前序,中序还是后序

代码实现

节点类

package com.datestructures.tree;


public class HeroNode {
    //节点类
    private int no;
    private String name;
    private HeroNode left;//默认为空
    private HeroNode right;//默认为空

    //构造器

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);//先输出父节点
        //向左递归
        if (this.left != null) {
            this.left.preOrder();
        }
        //向右递归
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
        System.out.println("进入前序遍历查找");
        //先判断当前节点是否为目标值
        if (this.no == no) {
            return this;
        }
        //定义一个res来接收结果
        HeroNode resNode = null;
        //如果不是向左递归
        if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }
        //如果找到就返回
        if (resNode != null) {
            return resNode;
        }
        //没有找到就向右递归
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
        //最后有没有找到都返回
        return resNode;
    }

    //中序遍历
    public void infixOrder() {
        //向左递归
        if (this.left != null) {
            this.left.infixOrder();
        }
        //输出当前节点
        System.out.println(this);
        //向右递归
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    //中序遍历查找
    public HeroNode infixOrderSearch(int no) {
        //定义一个结果来存放结果
        HeroNode resNode = null;
        //先判断左子节点是否为空
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }
        //如果resNode不为空,表明找到 返回即可
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入中序遍历查找");
        //为空  就和当前节点比较
        if (this.no == no) {
            return this;
        }
        //当前节点不是目标值 则向右查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }
        //最后返回
        return resNode;
    }

    //后序遍历
    public void postOrder() {
        //向左递归
        if (this.left != null) {
            this.left.postOrder();
        }
        //向右递归
        if (this.right != null) {
            this.right.postOrder();
        }
        //输出当前节点
        System.out.println(this);
    }

    //后序遍历查找
    public HeroNode postOrderSearch(int no) {
        //先定义一个结果
        HeroNode resNode = null;
        //向左递归
        if (this.left != null) {
            resNode = this.left.postOrderSearch(no);
        }
        //如果不为空 说明找到  返回即可
        if (resNode != null) {
            return resNode;
        }
        //否则向右递归
        if (this.right != null) {
            resNode = this.right.postOrderSearch(no);
        }
        //如果不为空 说明找到  返回即可
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入后序遍历查找");
        //否则和当前节点对比
        if (this.no == no) {
            return this;
        }
        return resNode;
    }
    //删除节点
    public void delNode(int no){
        //因为二叉树是单向的 所以先判断当前节点的子节点是否是要删除的节点,而不是先判断当前节点
        //先判断当前节点的子节点是否为要删除的节点
        if(this.left!=null&&this.left.no==no){
            this.left=null;
            return;
        }
        if(this.right!=null&&this.right.no==no){
            this.right=null;
            return;
        }
        //向左递归 进行删除
        if(this.left!=null){
            this.left.delNode(no);
        }
        //向右递归  进行删除
        if(this.right!=null){
            this.right.delNode(no);
        }
    }
}

二叉树类

package com.datestructures.tree;

public class BinaryTree {
    private HeroNode root;//根节点
    //初始化根节点

    public void setRoot(HeroNode root) {
        this.root = root;
    }
    //前序遍历
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空,不能遍历");
        }
    }
    //前序遍历查找
    public HeroNode preOrder(int no){
        if(root!=null){
            return this.root.preOrderSearch(no);
        }else{
            return null;
        }
    }
    //中序遍历
    public void infixOrder(){
        if(this.root!=null){
            this.root.infixOrder();
        }else{
            System.out.println("二叉树为空,不能遍历");
        }
    }
    //中序遍历查找
    public HeroNode infixOrderSearch(int no){
        if(root!=null){
            return this.root.infixOrderSearch(no);
        }else{
            return null;
        }
    }
    //后序遍历
    public void postOrder(){
        if(this.root!=null){
            this.root.postOrder();
        }else{
            System.out.println("二叉树为空,不能遍历");
        }
    }
    //后序遍历查找
    public HeroNode postOrderSearch(int no){
        if(root!=null){
            return this.root.postOrderSearch(no);
        }else{
            return null;
        }
    }
    //删除节点
    public void delNode(int no){
        if(root!=null){
            //如果只有一个节点,先判断这个节点是不是要删除的节点
            if(root.getNo()==no){
                root=null;
            }else {
                //递归删除
                root.delNode(no);
            }
        }else{
            System.out.println("空树,不能删除");
        }
    }
}

测试类

package com.datestructures.tree;

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //先创建一个二叉树
        BinaryTree binaryTree =new BinaryTree();
        //再创建四个节点
        HeroNode root = new HeroNode(1,"宋江");
        HeroNode hero2 = new HeroNode(2,"吴用");
        HeroNode hero3 = new HeroNode(3,"卢俊义");
        HeroNode hero4 = new HeroNode(4,"林冲");
        HeroNode hero5 = new HeroNode(5,"关胜");
        //再将几个节点关联起来
        root.setLeft(hero2);
        root.setRight(hero3);
        hero3.setRight(hero4);
        hero3.setLeft(hero5);
        //再将根节点赋予到树上
        binaryTree.setRoot(root);
        System.out.println("前序遍历");
        binaryTree.preOrder();//1 2 3 5 4
        System.out.println("中序遍历");// 2 1 5 3 4
        binaryTree.infixOrder();
        System.out.println("后序遍历");// 2 5 4 3 1
        binaryTree.postOrder();

        System.out.println("前序遍历方式");
        HeroNode resNode1 = binaryTree.preOrder(5);
        if(resNode1!=null){
            System.out.println("找到了,信息为 no = "+resNode1.getNo()+"  name = "+resNode1.getName());
        }else{
            System.out.println("没有找到编号为"+5+"的英雄");
        }
        System.out.println("中序遍历方式");
        HeroNode resNode2 = binaryTree.infixOrderSearch(5);
        if(resNode2!=null){
            System.out.println("找到了,信息为 no = "+resNode2.getNo()+"  name = "+resNode2.getName());
        }else{
            System.out.println("没有找到编号为"+5+"的英雄");
        }
        System.out.println("后序遍历方式");
        HeroNode resNode3 = binaryTree.postOrderSearch(5);
        if(resNode3!=null){
            System.out.println("找到了,信息为 no = "+resNode3.getNo()+"  name = "+resNode3.getName());
        }else{
            System.out.println("没有找到编号为"+5+"的英雄");
        }

        //删除测试
        System.out.println("删除前先遍历");
        binaryTree.postOrder();
        binaryTree.delNode(5);
        //binaryTree.delNode(3);
        System.out.println("删除后遍历");
        binaryTree.postOrder();
    }
}

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

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

相关文章

linux中使用docker部署微服务

目录 一、制作jar包(如果看一眼很简单,可以直接使用结尾的jar) 1.首先创建一个微服务 demo2 2.启动微服务(在DemoApplication上右键执行启动就行) 注意:其他操作导致的 可能遇到的报错 3.修改端口 4.新…

ChatGPT的快速发展究竟给我们带来了什么?

😚一个不甘平凡的普通人,致力于为Golang社区和算法学习做出贡献,期待您的关注和认可,陪您一起学习打卡!!!😘😘😘 🤗专栏:算法学习 &am…

java基础入门-05-【面向对象进阶(static继承)】

Java基础入门-05-【面向对象进阶(static&继承)】 13、面向对象进阶(static&继承)1.1 如何定义类1.2 如何通过类创建对象1.3 封装1.3.1 封装的步骤1.3.2 封装的步骤实现 1.4 构造方法1.4.1 构造方法的作用1.4.2 构造方法的…

Unity API详解——Random类

Random类是Unity中用于产生随机数的类,不可以实例化,只有静态属性和静态方法。本博客主要介绍了Random类的一些静态属性。 文章目录 一、Random类静态属性1、基本语法2、功能说明3、代码实现 二、rotationUniform属性1、基本语法2、功能说明1、规范化向量…

前沿探索,AI 在 API 开发测试中的应用

目录 一、引言二、AI 加持下的 API 设计1、NLP 在 API 设计中的应用2、DL 在 API 设计中的应用能力一:Apikit 如何利用 AI 生成最佳的 API 设计方案能力二: Apikit 如何利用 AI 提高 API 的可用性和易用性 三、AI 加持下的 API 开发能力三:Ap…

k8s二进制安装部署(详细)(3主2从)

目录 kubeadm 和二进制安装 k8s 适用场景分析 多 master 节点高可用架构图 集群环境准备 部署过程 修改主机内核参数(所有节点) 配置阿里云的repo源(所有节点) 配置国内安装 docker 和 containerd 的阿里云的 repo 源 配置…

比肩 ChatGPT,国内快速访问的强大 AI 工具 Claude

作者:明明如月学长, CSDN 博客专家,蚂蚁集团高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《EffectiveJava》独家解析》专栏作者。 热门文章推荐…

Gateway案例

官网:Spring Cloud Gateway 中文文档:Spring Cloud Gateway 2.1.0 中文官网文档 - 腾讯云开发者社区-腾讯云 一、网关介绍: 网关就是当前微服务的统一入口 通常在微服务项目中,只有网关项目是暴露在网络里的,其他服务一般都是在内网里, 用户访问网关,网关根据访问的路径,来进…

Tomcat安装步骤及详细配置教程(2022最新版)

网上的tomcat安装及配置教程一大堆,但是好多都过时了,根本不适用现在的版本,今天凯歌整理一篇Tomcat安装步骤及详细配置教程,2022年最新版~ Tomcat安装及配置教程主要分为四步: 步骤一:首先确认自己是否已…

ChatGPT登录操作扫盲级教程,附ChatGPT登录常见报错及处理技巧

文 / 韩彬(微信公众号:量子论) 有了帐号,我们自然可以使用ChatGPT尽情玩耍了。 知识扩展:ChatGPT是啥,以及注册的问题,可以看《ChatGPT常见问题手册,通俗易懂版,3分钟了解…

Windows微信聊天图片文件的解码和图片、视频占满电脑磁盘空间的清理

1 问题现象 我的Windows版微信最近老是提示“磁盘空间不足200MB,需及时清理磁盘”。 使用文件资源管理器查看我的电脑磁盘使用情况,发现C盘只剩下174MB空间可用。系统盘C盘空间耗尽已经严重影响电脑的使用。 2 问题分析 2.1 磁盘空间占用情况分析 由于…

【学习笔记】pandas提取excel数据形成三元组,采用neo4j数据库构建小型知识图谱

前言 代码来自github项目 neo4j-python-pandas-py2neo-v3,项目作者为Skyelbin。我记录一下运行该项目的一些过程文字以及遇到的问题和解决办法。 一、提取excel中的数据转换为DataFrame三元组格式 from dataToNeo4jClass.DataToNeo4jClass import DataToNeo4j imp…

实操带你使用Mybatis_plus(2)

文章目录 一、通用ServiceService CRUD 接口a> IServiceb>创建Service接口和实现类测试 二、常用注解1、TableName2、TableId雪花算法3、TableField4、TableLogic 一、通用Service Service CRUD 接口 通用 Service CRUD 封装IService 接口,进一步封装 CRUD …

选择无服务器:Babbel 的迁移故事

Babbel 是什么? Babbel 是一个完整的语言学习产品生态系统,囊括了世界上最畅销的语言学习应用程序。我们已售出超过 1000 万份订阅和超过 60,000 门涵盖 14 种语言的课程,创造了全球第一语言学习目的地。自 2007 年推出产品的第一天起&#…

vivid源码分析

vivid源码分析 文章目录 vivid源码分析如何编写V4L2驱动分析vivid.c的open,read,write,ioctl过程openreadioctlv4l2_ctrl_handler使用过程 如何编写V4L2驱动 分配/设置/注册v4l2_device.v4l2_device_register,v4l2_device(辅助作用,提供自旋锁,引用计数…

LeetCode单链表OJ题目做题思路分享

目录 移除链表元素链表的中间节点链表中倒数第K个节点合并两个有序链表 移除链表元素 链接: link 题目描述: 思路分享: 我们上个博客分享了第一种方法,下面我们分析第二种方法:思路就是将每一个不等于我们要删除的值的节点依次尾…

【硬件】嵌入式电子设计基础之产品实践

电子技术是一门实践性非常强的学科,学习电子元器件基础知识和设计技能,最终为的是把具备一定功能的电路板制作出来,解决科研、生产、生活中的实际问题。 本篇文章从实际的电子产品出发,让您能够初步体验电子产品的硬件设计过程&am…

【Unity编辑器】拓展Project视图

目录 1、拓展右键菜单 2、创建一个菜单 3、拓展布局 4、监听事件 首先创建一个Editor文件夹,此文件夹可以作为多个目录的子文件夹存在,这样开发者就可以按照功能来划分,将不同功能的编辑代码放在不同的Editor目录下。 如果属于编辑模式下…

多维时序 | MATLAB实现BP、SVM、LSSVM多变量时间序列预测(考虑历史特征的影响,多指标、多图输出)

多维时序 | MATLAB实现BP、SVM、LSSVM多变量时间序列负荷预测(考虑历史特征的影响,多指标、多图输出) 目录 多维时序 | MATLAB实现BP、SVM、LSSVM多变量时间序列负荷预测(考虑历史特征的影响,多指标、多图输出)预测效果基本介绍程序设计学习总结参考资料…

大规模并行处理架构Doris概述篇

目录 1 Doris概述篇1.1 前言1.2 Doris简介1.3 核心特性1.4 Doris特点1.5 Doris发展历程1.6 对比其他的数据分析框架1.7 开源OLAP引擎对比1.8 使用场景1.9 使用用户 2 Doris原理篇2.1 名称解释2.2 整体架构2.3 元数据结构2.4 数据分发 1 Doris概述篇 1.1 前言 Doris由百度大数据…