数据结构——树(一):二叉树

news2024/9/20 10:40:36

前言

        在这篇文章中,荔枝会整理一下自己学习二叉树的学习笔记。主要内容包括树与二叉树的基本定义以及基础概念、二叉树的存储结构、二叉树的四种遍历方法以及二叉查找树的基本内容。


文章目录

前言

一、树形存储结构 

二、二叉树

2.1 二叉树的基本定义

2.2 二叉树的存储结构 

2.2.1 顺序存储结构

2.2.2 链式存储结构 

2.3 二叉树的遍历

2.3.1 先序遍历

2.3.2 中序遍历

2.3.3 后序遍历

2.3.4 层次遍历 

三、二叉搜索树BST

3.1 二叉搜索树的查找

3.2 二叉搜索树的插入

3.3 二叉搜索树的删除 

总结


一、树形存储结构 

        树是一种非线性存储结构,主要的逻辑关系是一对多,从外形上看其实就相当于数目的根系。在树这一结构中的所有元素都可以称之为结点。结点之间的关系为父结点和子结点,没有父结点的结点称之为根结点;没有子结点的结点称之为叶结点。那么一个树可以看成为一个根结点和若干个子树构成,这若干个子树组成森林,因此一个树形结构可以看成一个根节点和森林组成。一个结点拥有的子树的个数称之为结点的度,树中结点层级的最大值被称为树的高度或深度。


二、二叉树

2.1 二叉树的基本定义

定义一个二叉树需要满足两个条件,首先需要是有序树,同时各个结点的度不超过2。通俗一点讲,在一个树形的数据结构中,如果该结构的任意节点的子节点个数不超过2且左右节点分支不能交换的就可以称之为二叉树。二叉树又分为:满二叉树和完全二叉树。

完全二叉树

        树的末端最后一层结点是按照从左到右来分布的。

2.2 二叉树的存储结构 

2.2.1 顺序存储结构

在二叉树的顺序存储结构中我们可以对一个结点进行编号为N,则其父节点就是N/2,子节点中的左节点为2N,右节点为2N+1。二叉树的顺序存储结构只能用于完全二叉树的应用。

特点:

对于完全二叉树来说,其顺序存储十分合适,但对于一般二叉树来说顺序存储结构会浪费系统较多的存储空间,但其对于寻找一个结点的双亲和孩子比较容易。

2.2.2 链式存储结构 

链式存储结构采用了链表这一数据结构作为基础存储,而且是双向链表,每一个子结点均拥有头指针和尾指针头指针指向左结点,右指针指向右结点。

  

特点:

除了指针外,二叉树比较节省存储空间,占用的空间大小只与节点的数量有关;在链式存储结构中,找一个结点的孩子很容易,找其双亲很难。

2.3 二叉树的遍历

二叉树的遍历主要有四种方法:先序遍历、中序遍历、后序遍历和层次遍历。以一个二叉树ABCDEFGHI为例,我们对比看看四种遍历方式

2.3.1 先序遍历

        先序遍历其实比较简单,遍历过程从根结点A开始,遵循先左后右的规则在二叉树中进行遍历。依次遍历A-B-C-H-I-D-E-F-G。在先序遍历中我们可以看出整个过程子结点的遍历顺序和根结点是一样的,因此我们可以考虑使用递归的方法来实现遍历的过程,而递归的结束条件就是当前结点不再有子结点,也就是当前结点是叶结点。前序遍历的过程其实就是不断执行从根节点到子节点的遍历

2.3.2 中序遍历

        中序遍历其实就是从当前的结点位置出发,首先进入结点的左子树进行遍历,依旧遵照这先左后右的规则首先进入左结点,若该子结点只有一层子树,继续进入子树结构并访问到左子树结点,结束访问左子结点之后会重新访问该结点,再接着向右侧子树进行遍历。遍历过程依次是:H-C-I-B-D-A-F-E-G。

2.3.3 后序遍历

        后序遍历的过程可以理解为和前序遍历的过程是相反的,前序遍历其实就是从根结点到叶结点,但后续遍历则是从叶结点到根结点。比如在上面那张图的二叉树中,我们遍历树中所有的元素的顺序是:H-I-C-D-B-F-G-E-A。具体过程是我们根据先左后右的规则借助不同层级的结点进入到最高层级的叶结点处并执行访问,在该层级中访问到所有的元素之后就会回退到上一层级C的结点并进入该结点的右子树执行重复操作,当完全访问完C所有的子树后就会回退到C并执行访问,对于不同层级之间都是以这种机制来执行回退访问的。

PS:不管是先序、中序还是后序排序,递归的终止条件其实都是当该结点是叶结点的时候不进行递归操作,直接输出并决定是否需要回退。 

2.3.4 层次遍历 

层次遍历用链表存储的二叉树,可以借助队列存储结构来实现。整个层次遍历过程其实是首先根结点入队,并进入下一层级找到子节点按照从左至右的方式入队,将上述的二叉树进行层次遍历的过程:A-B-E-C-D-F-G-H-I。


三、二叉搜索树BST

        二叉搜索树又称之为二叉查找树、有序二叉树、排序二叉树,二叉查找树相比于其它的数据结构的优势在于查找、插入的时间复杂度比较低,二叉查找树可用于构建更为抽象的数据结构,比如集合、多重集和关联数组。最好最坏的时间复杂度都是O(n)。

特点:

若任意结点的左右结点不为空,则左子树上的所有结点的值均小于他的根结点的值;右子树的值均大于或等于其根结点的值。值得注意的是:二叉查找树是没有重复的结点的。

3.1 二叉搜索树的查找

根据二叉搜索树的特殊结构,我们可以直接将要查询的数字num与根结点p进行比较,若num<p,我们就选择p的左结点作为p来跟查询的num比较;若num>p,我们就选择p的右结点作为p来跟查询的num比较,直到找到结点的位置。

3.2 二叉搜索树的插入

        二叉搜索树的插入过程和查找的过程其实是类似的,当我们在二叉搜索树搜索的数值并不在树中存储的时候,查找的结点会结束于一条空链这时候就会执行插入的操作。同时也会遵守左小右大的规则进行插入。比如要对num执行插入的操作,那么直接将num于根结点进行比较,如果根结点的值p比nu大,则将num插入左子树中,若左子树为空树,那么num就会以p的左节点的形式存在于二叉树中;对于num大于p的情形也大致相同。

3.3 二叉搜索树的删除 

        二叉搜索树的删除操作要比查找和插入的操作更加复杂,二叉搜索树的删除主要分为三种情况:要删除的结点P无子节点、要删除的结点P只有一个子结点、要删除的结点P有两个子节点。我们分别看看这三种情况:第一种是最简单的情况,直接将指向P的父节点指针指向null;第二种情况需要将P的父节点指针指向P的子节点;第三种情况最为复杂,我们需要找到P的右子树的最小结点或者是左子树的最大结点并将P的父节点的指针指向该结点。

再来看一下大佬的代码辅助理解:

public class BinarySearchTree {
    private Node tree;

    /**
     * 查找
     * @param data
     * @return
     */
    public Node find(int data) {
        Node p = tree;
        while (p != null) {
            if (data < p.data) p = p.left;
            else if (data > p.data) p = p.right;
            else return p;
        }
        return null; //没有找到
    }
    
    /**
     * 插入
     * @param data
     */
    public void insert(int data) {
        if (tree == null) {
            tree = new Node(data);
            return;
        }
        
        Node p = tree;
        while (p != null) {
            if (data > p.data) {
                if (p.right == null) {
                    p.right = new Node(data);
                    return;
                }
                p = p.right;
            } else { //data < p.data
                if (p.left == null) {
                    p.left = new Node(data);
                    return;
                }
                p = p.left;
            }
        }
    }
    
    /**
     * 删除
     * @param data
     */
    public void delete(int data) {
        Node p = tree; //p 指向要删除的结点,初始化指向根节点
        Node pp = null; //pp 记录的是 p 的父节点
        
        while (p != null && p.data != data) {
            pp = p;
            if (data > p.data) p = p.right;
            else p = p.left;
        }
        if (p == null) return; //没有找到
        
        //要删除的节点有两个子节点
        if (p.left != null && p.right != null) {//查找右子树中最小的节点
            Node minP = p.right;
            Node minPP = p; //minPP 表示 minP 的父节点
            while (minP.left != null) {
                minPP = minP;
                minP = p.left;
            }
            p.data = minP.data; //将 minP 的数据替换到 p 中
            p = minP; //下面就变成了删除 minP 了,要结合整个删除函数来看
            pp = minPP;
        }
        
        //删除的是叶子节点或者仅有一个子节点
        Node  child; //p 的子节点
        if (p.left != null) child = p.left;
        else if (p.right != null) child = p.right;
        else child = null;
        
        if (pp == null) tree = child; // 删除的是根节点
        else if (pp.left == p) pp.left = child;
        else pp.right = child;
    }
    
    /**
     * 查找最小节点
     * @return
     */
    public Node findMin() {
        if (tree == null) return null;
        Node p = tree;
        while (p.left != null) {
            p = p.left;
        }
        return p;
    }
    
    /**
     * 查找最大节点
     * @return
     */
    public Node findMax() {
        if (tree == null) return null;
        Node p = tree;
        while (p.right != null) {
            p = p.right;
        }
        return p;
    }
    
    private static class Node {
        private int data;
        private Node left;
        private Node right;
        
        public Node(int data) {
            this.data = data;
        }
    }
}

附上代码原文链接:

https://www.jianshu.com/p/d1133ef8bc0e


总结

  在这篇文章中,荔枝主要做了一下有关二叉树的笔记,希望不久之后我也能写一个完整功能的二叉树哈哈哈,个人感觉要掌握新的算法或者数据结构还是得不断地在刷题中掌握知识,总之荔枝会努力向大佬学习的🤩

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

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

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

相关文章

【项目笔记】尚硅谷《云原生实战》

尚硅谷《云原生实战》1、云服务器nginx测试2、子网3、Docker4、Redis尚硅谷云原生项目 官方笔记 1、云服务器nginx测试 在云服务器供应商购买云服务器&#xff0c;配置弹性公网ip&#xff1a;121.37.230.200。外部访问使用公网ip&#xff0c;弹性公网每次会变&#xff1b;服务…

Docker中安装并配置单机版redis

1、使用docker安装redis 搜索Reis镜像&#xff0c;这里展示的是官方最新的镜像docker search redis 使用官方dockerhub搜索redis 2、选用常用的redis5.0作为安装的版本docker pull redis:5.0 3、运行redis容器的两种方式 3.1 不映射外部配置文件直接运行redis5.0镜像docker …

【C#基础】C# 正则表达式

序号系列文章7【C#基础】C# 常用数据结构8【C#基础】C# 面向对象编程9【C# 基础】C# 异常处理操作文章目录前言1&#xff0c;Regex 的概念2&#xff0c;Regex 的创建3&#xff0c;Regex 常用操作4&#xff0c;Regex 类的使用5&#xff0c;学习资源推荐结语前言 &#x1f33c; h…

软件测试之【性能测试】

性能测试的定义 性能测试的定义&#xff1a;通过自动化测试工具或者代码手段&#xff0c;来模拟正常、峰值负载访问被测系统&#xff0c;来观测系统各项性能指标是否合格的过程。 性能测试的分类 基于代码的性能测试&#xff08;关注点是函数或方法执行的效率&#xff09; 基于…

acwing1562 微博转发(宽搜)

微博被称为中文版的 Twitter。 微博上的用户既可能有很多关注者&#xff0c;也可能关注很多其他用户。 因此&#xff0c;形成了一种基于这些关注关系的社交网络。 当用户在微博上发布帖子时&#xff0c;他/她的所有关注者都可以查看并转发他/她的帖子&#xff0c;然后这些人…

铰链、弹簧,特殊的物理关节

title: 铰链、弹簧&#xff0c;特殊的物理关节 date: 2023-02-28T13:32:57Z lastmod: 2023-02-28T14:24:06Z 铰链关节&#xff08;Hinge Join&#xff09;组件 组件-Physics-Hinge Join Anchor 当物体挂载铰链组件以后&#xff0c;组件下Anchor等同于边长为1的立方体。当这…

机器学习笔记之流形模型——标准流模型基本介绍

机器学习笔记之流形模型——标准流模型基本介绍引言回顾&#xff1a;隐变量模型的缺陷标准流(Normalizing Flow\text{Normalizing Flow}Normalizing Flow)思想分布变换的推导过程引言 本节将介绍概率生成模型——标准流模型(Normalizing Flow\text{Normalizing Flow}Normalizi…

第九节 常用API(String/ArrayList)

常用API(String/ArrayList) java写好的程序&#xff0c;我们可以直接调用。 String类定义的变量可以用于存储字符串&#xff0c;同时String类提供了很多操作字符串的功能&#xff0c;我们可以直接使用。 ArrayList简单介绍 1.ArrayList代表的是集合类&#xff0c;集合是一种容…

《C++ Primer Plus》(第6版)第6章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第6章编程练习《C Primer Plus》&#xff08;第6版&#xff09;第6章编程练习1. 大小写转换2. 平均值3. 菜单4. 成员5. 收入所得税6. 捐款7. 统计单词8. 统计文件字符数9. 重写编程练习6《C Primer Plus》&#xff08;第6版&…

taobao.top.secret.appkey.bill.detail( 服务商解密账单查询 )

&#xffe5;免费不需用户授权 服务商解密账单查询,分页返回所有店铺的账单&#xff0c;每个店铺每天仅包含两条数据&#xff0c;当天产生的号租费 和 当天产生的通话费&#xff0c;仅对90天内的账单提供SLA保障。查询账单详情请使用taobao.top.secret.bill.detail接口。 公共参…

计算机的发展

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。个人爱好: 编程&#xff0c;打篮球&#xff0c;计算机知识个人名言&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石…

云打包苹果证书生成、上架和应用截屏攻略

在使用apicloud或hbuilderx这些跨端的开发工具开发移动应用的时候&#xff0c;假如是打包ios应用&#xff0c;是需要生成苹果证书、证书profile文件&#xff0c;和对应用上架的。首先要普及一个概念&#xff0c;苹果的应用是无法像安卓那样挂在自己的服务器上下载直接安装就可以…

C++ STL:迭代器 Iterator

文章目录1、迭代器的类型2、traitsiterator_traitstype_traits泛化的指针&#xff0c;容器与算法的桥梁。提供一种方法&#xff0c;按照一定顺序访问一个聚合对象中各个元素&#xff0c;而又不暴露该对象的内部表示。既能对容器进行遍历&#xff0c;又可以对外隐藏容器的底层实…

谷歌邮箱账号不会注册?注册失败?这份完美注册教程请收好

谷歌邮箱相信大家都不陌生吧&#xff0c;无论是用于发送和接收邮件&#xff0c;还是用于在国外网站注册&#xff0c;很多人都离不开谷歌邮箱。甚至&#xff0c;一些网站直接提供谷歌邮箱登录选项。这就是为什么很多跨境人想要注册谷歌邮箱的原因。 但是&#xff0c;大部分网友都…

Java学习之路002——面向对象编程

【说明】部分内容来源于网络&#xff0c;如有冲突&#xff0c;请联系作者删除。 一、面向对象编程(OOP) 2.1 对象和类的关系 2.2 面向对象的特征 2.2.1 封装 2.2.2 继承 2.2.3 多态 3、抽象 使用abstract关键字修饰的类或者方法 定义抽象类(使用abstract) // 1、定义抽象方法…

计数排序.

1.动图演示&#xff1a; 2.代码示例&#xff1a; package test1;import java.util.Arrays;public class Test3 {public static void main(String[] args) {int[] nums new int[]{1, 2, 1, 4, 52, 1, 4};int[] arr count_sort(nums, getMax(nums));for (int i : arr) {System…

spring boot 配合element ui vue实现表格的批量删除(前后端详细教学,简单易懂,有手就行)

目录 一.前言&#xff1a; 二. 前端代码&#xff1a; 2.1.element ui组件代码 2.2删除按钮 2.3.data 2.4.methods 三.后端代码&#xff1a; 一.前言&#xff1a; 研究了其他人的博客&#xff0c;找到了一篇有含金量的&#xff0c;进行了部分改写实现前后端分离&#xff0…

【Python实战】爬虫教程千千万,一到实战全完蛋?今天手把手教你一键采集某网站图书信息数据啦~排名第一的竟是...(爬虫+数据可视化)

前言 ​一本本书&#xff0c;是一扇扇窗&#xff0c;为追求知识的人打开认知世界的窗口 一本本书&#xff0c;是一双双翅膀&#xff0c;让追求理想的人张开翅膀翱翔 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末…

机器学习的特征归一化Normalization

为什么需要做归一化&#xff1f; 为了消除数据特征之间的量纲影响&#xff0c;就需要对特征进行归一化处理&#xff0c;使得不同指标之间具有可比性。对特征归一化可以将所有特征都统一到一个大致相同的数值区间内。 为了后⾯数据处理的⽅便&#xff0c;归⼀化可以避免⼀些不…

Pycharm和跳板机 连接内网服务器

Pycharm和跳板机 连接内网服务器 建立配置文件 本地配置 .ssh 文件夹下配置 config 文件 Host jumpHostName xxxPort 22User xxxServerAliveInterval 30IdentityFile C:\Users\15284\.ssh\id_rsa # 通过密钥连接Host server # 同样&#xff0c;任意名字&#xff0c;随…