上篇文章我们主要介绍了线性数据结构,本篇233酱带大家康康 无所不在的非线性数据结构之一:树形结构的特点和应用。

树形结构,是指:数据元素之间的关系像一颗树的数据结构。我们看图说话:



它具有以下特点:

  1. 每个节点都只有有限个子节点或无子节点;
  2. 没有父节点的节点称为根节点;
  3. 每一个非根节点有且只有一个父节点;
  4. 除了根节点外,每个子节点可以分为多个不相交的子树;
  5. 树里面没有环路(cycle)

维基百科中列举了计算机科学中树形结构的种类



233酱当然不会一个个讲,我们只挑一些熟悉的面孔:多叉树,二叉树,二叉查找树,红黑树,堆,Trie树,B树,B+树,LSM Tree,了解他们在对不同规模的数据 增,删,改,查 时所起到的作用就够了。

限于篇幅,本文主要介绍非LSM Tree的内容。

多叉树

树体现了一种 继承 的关系,节点之间为父子关系。多叉树 是指一个父节点可以有多个子节点。也就是:爸爸可以有多个儿子,儿子只能有一个爸爸。



当需要这种分层,继承的场景下均可以考虑用树结构来实现,可以简化我们对数据关系的描述。

此外,节点的每个分支也代表着一种选择,可以用来做决策。

应用场景:

1.Linux文件系统

2.组织关系表示,如公司的组织架构,角色权限系统等。

3.XML/HTML数据。

4.类的继承关系

5.决策,如游戏中怪物使用的技能选择,机器学习...

二叉树

二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于2的节点)的树结构,也就是说 爸爸 最多只能有 两个儿子。



因为计算机应用中存在很多“非黑即白”的场景,同样我们可以利用 不是走左分支,就是走右分支 这种结构选择来做一些决策。

另外,利用每个节点下参与方最多为两个,也可以做一些事情。

应用场景:

1.编译器的语法树构造。

2.表达式求值和判断:非叶子节点为操作符,叶子节点为数值。

3.Boolean求值,如(a and b)or (c or d)。

4.霍夫曼编码

5.IPv4路由表的存储...

在我们的日常开发中,经常需要对数据进行增删改查,每一个步骤的时间空间效率决定了应用最终的性能。

时间复杂度 体现在 能否快速定位到要操作的数据元素,核心在于 索引的好坏;

空间复杂度 体现在 能否占用 更小的内存空间,能否利用计算机各层介质的缓存性能提高访问速度(访问速度:CPU>> 内存>>磁盘)。

在以下树形结构的讨论中,希望小伙伴能多从 索引,所占用内存空间,操作的介质 这些方面考虑数据的增删改查性能。

二叉查找树

二叉查找树(Binary Search Tree)首先是二叉树,同时满足一定的有序性:节点的左子节点比自己小,节点的右子节点比自己大。



这样当我们定位一个元素的位置时,从根节点开始查找,小于节点左拐,大于节点右拐。等于节点排序值就刚好找到。二分的索引思想就体现在其中。

应用场景:

二叉查找树的有序性是它能够广泛应用的原因。但是能否高效二分体现在树的高度合理性上。下面要讲的 红黑树/堆结构才是其广泛应用。

红黑树

二叉查找树的缺点在于:只限制了节点的有序性,但有序树的构造有好坏。一颗“坏”的有序树直接会被拉成 “有序链表”。所以需要通过一定的条件保证树的平衡性。

树的平衡性是指整棵树的最高子树和最矮子树相差不大,这样整棵树的高度相对来说低一些,相应的增,删,改,查操作的效率较高较稳定(与树高有关)。

红黑树(Red–black tree)是应用很广泛的一种平衡树,是面试官的装X利器。我们来看一下它保证平衡性的一些特性:

  1. 节点是红色或黑色。
  2. 根是黑色。
  3. 所有叶子都是黑色(叶子是NIL节点)。
  4. 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
  5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

这些约束确保了红黑树的关键特性:从根到叶子的最长路径不多于最短路径的两倍长(根据性质4和性质5)。从而整棵树的高度比较稳定,相应的增、删、改、查操作的效率较高较稳定,而不同于普通的二叉查找树。

此外相比其他的平衡树:如高度平衡树AVL树,红黑树的增删改效率较高,同时查找性能没有下降很多也比较稳定。所以工业级应用更为广泛。

应用场景:适合排序,查找的场景。

1.容器的基本组成,如Java中的HashMap/TreeMap.

2.Linux内核的完全公平调度器

3.Linux中epoll机制的实现...

堆

堆是一种特殊的数据结构,它满足两个特性:

  1. 堆是一个完全二叉树;
  2. 堆中每一个节点的排序值都必须大于等于(或小于等于)其左右子节点的排序值。

这里解释以下完全二叉树。它是指:除了最后一层,其他层的节点个数都是满的,最后一层的节点都靠左排列。

这样我们就可以用数组来存储。

假设根节点在i=0的位置:如果父节点的数组下标为i,则左子节点的数组下标为2 * i+1,右子节点的数组下标为 2 * i + 2。数组比链表的存储好处上篇文章233酱提过了,这里就不赘述了。

针对第2点,如果每个节点的值都大于等于子树中每个节点值的堆,叫做“大顶堆”。反之叫做“小顶堆”。

堆这种结构相对有序,且堆顶元素要么最小,要么最大。且利用了 数组和自身特性 增删改查的时间复杂度较低。

应用场景:

1.堆排序

2.TopK,求中位数,求

3.合并多个有序小文件或集合

4.优先队列,如定时任务的存储等...

Trie树

Trie树 又称前缀树或字典树,它是一种专门处理字符串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题。

它的特性为:

  1. 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
  2. 从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。
  3. 每个字符串的公共前缀作为一个字符节点保存。

如果我们有and,as,at,cn,com这些关键词,那么构建的trie树如下:

Trie 树的本质,就是利用字符串之间的公共前缀,将重复的前缀合并在一起。这样当我们查找某个字符串时,只需要在Trie树上匹配字符串的每个字符,比较次数仅和 字符串的长度 有关。

但是Trie 树的缺点就是构造Trie树需要很大的内存空间。因为父子字符节点之间用 指针关联。如果用数组保存这些指针,这意味着子节点的数组需要穷举出每一种可能。如子节点字符为a-z,就需要分配长度为26的数组来存储这些可能的子节点。

改进内存分配的措施有:

1.子节点指针改为用:有序数组、跳表、散列表、红黑树来存储。

2.子节点合并,如原来 hello存储为:h->e->l->l->o ,现在可存储为:h->e->llo

Trie 树毕竟比较浪费空间,所以它在字符串的查找中,适合用于:1.字符集不能太大 2.字符串的公共前缀较多 的场景。

应用场景:

1.关键词匹配,提示,纠错。

2.最长公共前缀匹配。

3.命令自动补全,如zsh.

4.网址浏览历史记录。

5.手机号码簿查询...

B树、B+树、LSM Tree是数据库中经常出现的数据结构。对于数据库的增删改查效率,需要考虑的首要因素是:磁盘的IO访问次数。

如何减少IO访问次数?

前文我们已经提到了索引,但是IO一次不容易,从磁盘中获取数据通常是以块为单位的。如果对于上千万条数据,我们只建立一层索引结构的话,那索引的数据量也是巨大的。

如何降低索引量?

假设我们到全世界找一个人,我们是按照 国家/省/市/区/街道/小区的顺序来定位。同理,随着数据量的增大,我们需要一层层的建立 多级索引 。越上层的索引每个块中表示的数据范围越大。

如何保证我们建立的多级索引 是“合适”的?

这个合适主要指:存储的数据量大并且树高小。同红黑树一样,我们需要一些 限制条件 来保证树高。这也就是以下数据结构的限制条件原因了。

B树

一个m阶(该树每个节点最多有 M 个子节点)的B树具有以下特征:

1.根节点至少有两个子女。

2.每个中间节点都包含k-1个元素和k个孩子,其中 m/2 <= k <= m

3.每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m

4.所有的叶子节点都位于同一层。

5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。

一个3阶的B树插入示意图如下:

应用场景:MongoDB

B+树

一个m阶的B+树具有以下特征:

1.有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。

2.所有的叶子节点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子节点本身依关键字的大小自小而大顺序链接。

3.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

看不懂没关系,我们只需要知道这些限制条件是为了让B+树数据“矮而胖”就好。

这里我直接放张掘金小册《从根儿上理解MYSQL》B+树主键索引的示意图:

应用场景

1.Mysql InnoDB存储引擎。

看到这里常考面试题来了:B树和B+树有什么区别?为什么Mongo用B树?为什么Mysql用B+树?

B树 vs B+树

看图说话,B树 和 B+树显著不同的地方是:

1.B树非叶子节点即是索引,也是数据;B+树非叶子节点仅是索引,数据全部存储在叶子节点。

2.B+树叶子节点的数据之间是用链表链接的。

这会导致:

B+树相比B树:

1.数据的连续性:

  • B+树叶子节点上一页存储的数据是连续的,当需要一个结点上的数据时,B+树可以增大缓存的命中率。

2.叶子结点之间的连接性:

  • 当作范围或全文扫描时,B+树可以依赖叶子结点做线性顺序扫描,而B树只能在每一层的结点上做扫描。B+树同样可以增大缓存的命中率。

B树相比B+树:

当作单一数据查询时,B树的结点平均离根结点更近,平均查询效率比B+树快。

总结一下:B+树相比B树,前者更适合范围查询,后者更适合单一数据查询。

Mongo是非关系型数据库,数据之间的关系用嵌套解决。它的主要使用场景是: 追求 单个读写记录的性能。

Mysql是关系型数据库,数据之间的关系用共同的索引键,Join解决。它的使用场景:不仅存在大量的单一数据查询,也存在大量的范围查询。

所以上面的问题可以简单扯一扯了吧。

下篇文章233酱准备介绍近几年数据库中经常出现的角色:LSM Tree。觉得本文有收获 or 期待下篇 请四连【关注,点赞,在看,转发】支持下233吧~

参考资料:

[1].维基百科

[2].https://www.youtube.com/watch?v=OJ5NYq1Eii8

[3].https://time.geekbang.org/column/article/72414

[4].https://towardsdatascience.com/8-useful-tree-data-structures-worth-knowing-8532c7231e8c

[5].https://draveness.me/whys-the-design-mongodb-b-tree/

浅谈树形结构的特性和应用(上):多叉树,红黑树,堆,Trie树,B树,B+树...的更多相关文章

  1. 浅谈XXE漏洞攻击与防御——本质上就是注入,盗取数据用

    浅谈XXE漏洞攻击与防御 from:https://thief.one/2017/06/20/1/ XML基础 在介绍xxe漏洞前,先学习温顾一下XML的基础知识.XML被设计为传输和存储数据,其焦点 ...

  2. 浅谈html5及其新特性

    什么是 HTML5? HTML5 将成为 HTML.XHTML 以及 HTML DOM 的新标准. HTML 的上一个版本诞生于 1999 年.自从那以后,Web 世界已经经历了巨变. HTML5 仍 ...

  3. 浅谈PHP7的新特性

    我以前用过的php的最高版本是php5.6.在换新工作之后,公司使用的是PHP7.据说PHP7的性能比之前提高很多.下面整理下php7的新特性.力求简单了解.不做深入研究. 1.变量类型声明 函数的参 ...

  4. c#核心基础 - 浅谈 c# 中的特性 Attribute)

    特性(Attribute)是用于在运行时传递程序中各种元素(比如类.方法.结构.枚举.组件等)的行为信息的声明性标签.可以通过使用特性向程序添加声明性信息.一个声明性标签是通过放置在它所应用的元素前面 ...

  5. 浅谈HTML5的新特性

    2014年10月29日,W3C宣布,经过接近8年的艰苦努力,HTML5标准规范终于制定完成. HTML5将会取代1999年制定的HTML 4.01.XHTML 1.0标准,使网络标准达到符合当代的网络 ...

  6. 1.2浅谈Spring-Spring结构

    时隔很多天的我又回来....最近发展了一下自己的爱好,所以拖了很长时间. 前面我们从概念性上分析了spring的特性 这里我们附上Spring框架的结构图 我们简单的来说一些这个框架图 我们从下往上看 ...

  7. 浅谈 JVM 结构体系、类加载、JDK JRE JVM 三者的关系

    一.java类,创建.编译.到运行的工程: 1.随便建一个Java类,保存后就是一个.java文件, 2.然后我们使用 javac命令编译 .java文件,生产 .class文件. 3.再然后使用 j ...

  8. 浅谈Java的主要学习要点_上海尚学堂java培训课程思维导图

    Java是一种可以撰写跨平台应用程序的面向对象的程序设计语言.Java 技术具有卓越的通用性.高效性.平台移植性和安全性,广泛应用于PC.数据中心.游戏控制台.科学超级计算机.移动电话和互联网,同时拥 ...

  9. 浅谈抓取网页数据(奉上Demo)

    Demo源码 背景 曾经在公司做过一个比价系统,就是抓取其它网站上商品的价格并和自己公司的商品进行对应,然后展示出来,给pm提供一个定价的参考.后来,有同事的朋友在找工作的时候,猎头让其做一个抓取去哪 ...

随机推荐

  1. 【状压DP】SCOI2005-洛谷P1896-互不侵犯 (状压例题)

    [状压DP]SCOI2005-洛谷P1896-互不侵犯 (状压例题) 标签(空格分隔): 状压DP 好久没写博客了,真的爽(误) 题目: 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方 ...

  2. Python——操作smb文件服务器(上传和下载)

    最近在做上传和下载,然后文件比较大和多,就用到了文件服务器,文件服务器是实体机 ,不是在本地, 然后用python 通过pysmb模块就可以直接进行操作 mac选择前往.连接服务器去查看文件服务器里都 ...

  3. Bootstrap 搭建基础页面

    基于Bootstrap实现下图所示效果的页面,一个居中的标题和一个大按钮: <!DOCTYPE html> <html lang="zh-cn"> < ...

  4. Java入门系列之重写

    前言 关于所有Java系列文章面向有一定基础的童鞋,所写每一篇希望有一定含金量,有些内容可能会从Java整个语法全局考虑穿插后续要讲解的内容以成系统,若不理解,请看完后再学习.上一节我们讲解完了fin ...

  5. java 面向对象(十二):面向对象的特征二:继承性 (一) 前言

    1.为什么要有类的继承性?(继承性的好处) * ① 减少了代码的冗余,提高了代码的复用性 * ② 便于功能的扩展 * ③ 为之后多态性的使用,提供了前提图示: 2.继承性的格式:class A ext ...

  6. Python并发编程01 /操作系统发展史、多进程理论

    Python并发编程01 /操作系统发展史.多进程理论 目录 Python并发编程01 /操作系统发展史.多进程理论 1. 操作系统 2. 进程理论 1. 操作系统 定义:管理控制协调计算机中硬件与软 ...

  7. Python Ethical Hacking - Basic Concetion

    What is Hacking? Gaining unauthorized access. Hackers? 1.Black-hat Hackers 2.White-hat Hackers 3.Gre ...

  8. Oracle版本发布规划 (文档 ID 742060.1)

    Oracle Database Release Schedule of Current Database Releases (文档 ID 742060.1) Oracle Database RoadM ...

  9. 【Docker】Redis 安装使用教程

    1.安装 1.1 拉取镜像 docker pull redis redis:4.0 1.2 创建redis容器名"redistest1",并开启持久化 docker run -d ...

  10. Java冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序

    冒泡排序   冒泡排序是一种简单的排序算法.它重复地走访过要排序地数列,一次比较两个元素,如果它们地顺序错误就把它们交换过来.走访数列地工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成. ...