常见数据结构

常见数据结构

线性数据结构

数组

在内存中开辟一组连续的空间来存储相同类型的数据

特点:访问效率高,增删效率低

访问效率高:在内存中连续的,可通过下标直接查找到对应的元素

增删效率低:数组一旦初始化后大小是不可变的,

  1. 如果增添新的元素的大小大于创建的数组大小,需要重新创建新的数组,将原数组的数据复制到新数组中
  2. 若添加新元素到指定为止,则需要创建一个新数组,将索引对应之后的数组往后挪一位,并赋值新数组,然后将新添加的元素赋值到对应的索引上

队列

只允许一端进行插入,另一端进行删除的线性表

特点:先进先出,后进后出

可以想象成一个链表,允许插入的是链表尾,允许删除的是链表的头。

链表

链表也是线性顺序存储数据。只是在内存地址上不是连续的,也就是我所说的逻辑有序

单向链表

表头为空,每个节点会存储跳转到下一个节点的指针和该节点对应的数据,如图所示:

image.png

  1. 删除节点

    image.png

  2. 添加节点

    image.png

双向链表

双向链表是链表的一种,每个节点由两个指针分别指向直接前驱和直接后继,一般我们都是构造双向循环链表

  1. 删除

image.png

  1. 添加

    image.png

栈是限定只在表尾进行插入或删除的线性表,对于栈来说,对于栈来说,表位端称为栈顶(top),表头端称为栈低(bottom)。不含元素的空表成为空栈,因为栈限定在表尾进行插入或者删除,所以栈又被称为后进先出的线性表(简称LIFO:Last in,Last out 结构)

java中的栈是使用数组来实现的

非线性数据结构

非线性表:与线性表对立,数据之间并不是简单的前后关系。非线性表结构:二叉树,堆,图等

树就是n个节点的有限集

  1. 有且仅有一个根节点
  2. 子树的个数没有限制,但不能相交
  3. 度:节点拥有子树的个数则称为结点的度
  4. 普通树:

image.png

  1. 度数

image.png

  1. 节点关系:
  • 孩子节点
  • 双亲节点
  • 兄弟结点
  1. 节点层次

    image.png

    树的深度:树中节点的最大层次为4

二叉树

概念
  1. 二叉树:每个子节点只有两个节点的树,每个结点之多有两棵子树(二叉树中不存在度大于2的节点)

  2. 二叉查找树:也叫有序二叉查找树,一般性质如下

    1. 没有键值相等的节点
    2. 左子树节点值 <根节点值<右子树节点值
    3. 任意节点的左右子树都是二叉查找树
  3. 二叉树分类:

    1. 完美二叉树:又称为满二叉树,除叶子节点的每个节点都是两个孩子节点

      image.png

    2. 完全二叉树:除最后一层,其他每一层都被完全填充,并且所有的节点都保持向左对齐

      image.png

    3. 完满二叉树:除了叶子节点之外每个节点都有两个孩子节点

      image.png

遍历操作

image.png

  1. 中序遍历(左-根-右),访问顺序:1-2-2.5-3-4-5-5.5-6-7
  2. 先序遍历(根-左-右),访问顺序:4-2-1-3-2.5-6-5-5.5-7
  3. 后续遍历(左-右-根),访问顺序:1-2.5-3-2-5.5-5-7-6-4

查找最小值:沿着根节点的左子树一路查找直到最后一个不为空的节点,该节点就是当前这个树的最小节点

查找最大值:沿着根节点的右子树一路查找直到最后一个不为空的节点,该节点就是当前这个树的最大值

查找前驱节点:小于当前节点的最大值

查找后继节点:大于当前节点的最小值

删除节点

二叉树中的删除节点:本质上是找前驱节点或者是后继节点来替代

  • 叶子节点直接删除
  • 只有一个子节点的就用子节点替代(本质就是找前驱节点或后继节点,左-前驱,右-后继)
  • 只有两个子节点,需要找到替代节点(替代节点是前驱节点或者后继节点)
查找局限性

当一个二叉查找树全部右倾或左倾则会退化成链表(二叉树就是取了链表和数组的折中),二叉树的退化会影响查找的效率

image.png

AVL树

BST(Binary Search Tree)存在的问题是树在插入的时候一些情况会导致倾斜,不同的插入顺序会导致树的高度不一样,而树的高度会直接影响树的查找效率。最差的情况就是二分查找树所有节点在一条线上,也就是退化为链表,这样的树高度为N。

为了解决这个问题,BST(Blanced Search Tree平衡查找二叉树)产生了。

BST特性:插入和删除时,会通过旋转操作将高度保持在LogN

具有代表性的平衡树则有以下:

  1. AVL树:也称高度平衡树,它具备二叉搜索树的所有特性,并且左右子树的高低不超过1。

    AVL是通过左旋或者右旋来实现平衡,具体如下图所示:

    image.png

    虽然通过左旋或右旋解决的二叉树所存在的倾斜问题,但是由于AVL树过于严格,左旋和右旋频繁导致开销会比较大,因此我们红黑树诞生了。

  2. 红黑树:由2-3-4树演变而成,在java中treeMap本质则是红黑树,因此要了解红黑树建议先理解2-3-4树。

2-3-4树

概念

2-3-4树是4阶的B树,它属于一种多路查找树,它的结构有以下限制:

所有叶子结点都拥有的相同深度。

节点只能是2、3、4节点之一,以下为节点相关概念

  • 2节点:包含1个元素,有2个子节点
  • 3节点:包含2个元素,有3个子节点
  • 4节点:包含3个元素,有4个子节点

所有节点必须至少包含1个元素

元素整体保持有序,有着二叉查找树的性质:左子节点 < 父节点 < 右子节点

当节点有多个元素时,所有元素都必须大于它左边或者左子树的元素

image.png

生成过程
  1. 第一次插入2

    image.png

  2. 插入二节点-3节点合并

    image.png

3.插入第三节点-4节点合并

image.png

  1. 插入第四个节点-因为4个元素所以需要分裂

    image.png

  2. 插入6

    image.png

  3. 插入7,分裂

image.png

  1. 插入8

    image.png

  2. 插入9,超过3个元素分裂

    image.png

  3. 插入10,合并

    image.png

  4. 插入11,超过3个元素,分裂

    image.png

和红黑树的等价关系

红黑树起源于2-3-4树,本质上就是2-3-4树

2节点

2节点->黑色节点

image.png

3节点

3节点->可以有两种情况的红黑树节点,一种是右倾、一种是左倾,原则:上黑下红

image.png

4节点

4节点->中间节点为黑色,左右节点为红

image.png

裂变状态

在2-3-4树裂变状态的时候,转为红黑树后会先变色(相邻不能两个节点不能是红色,根节点为黑色)

image.png

转换为红黑树
  • 原始2-3-4树

image.png

  • 右倾

    image.png

  • 左倾

    image.png

红黑树
概念

红黑树(Red-Black-Tree),RBT是个自平衡(非绝对平衡)的二分查找树(BST),书上每个节点有以下规则

  1. 每个节点非红即黑
  2. 根节点为黑
  3. 每个叶子节点(NIL)是黑色
  4. 每个红节点的两个子节点一定为黑色
  5. 任意节点到每个叶子节点的路径都包含相同数量的黑色节点

红黑树自平衡主要是靠以下操作:

操作描述
左旋以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,
右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
右旋以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,
左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
变色结点的颜色由红变黑或由黑变红。
旋转操作
概念
  • 左旋

    以某个节点为旋转点,右子节点变成旋转节点的父节点,右子节点的左子节点变成旋转节点的右子节点

    image.png

  • 右旋

    以某个节点为旋转点,左子节点变成旋转节点的父节点,左子结点的右子节点变成旋转节点的左节点

    image.png

添加操作
  1. 普通二叉树添加节点
  2. 旋转变色
删除操作
  1. 普通二叉树删除操作
  2. 旋转变色

具体操作流程如下:

https://www.processon.com/diagraming/65a5f761ac3a9150f2aa70af

对应代码如下:

https://gitee.com/leiv8x/rbt_learn

B树

由来

传统用来搜索的平衡二叉树很多,例如AVL、红黑树,这些树在一般情况下查询性能非常好,但是数据量非常的大的时候就不行了。

原因:

数据量非常大时,内存不够用,大部分数据只能以存放在磁盘上,然后通过IO形式读取磁盘加载到内存中(内存访问-50ns,磁盘-10ms),读取磁盘时间远远超过数据在内存中比较的时间。所以大部分时间都阻塞在了IO上,因此要提高程序的性能就需要减少IO的次数。

  • 如果使用平衡二叉树来解决:

    因为平衡二叉树是通过自旋的方式来保持平衡,若旋转会对整棵树的操作

    1. 若数据过大,磁盘加载部分数据到内存则无法进行自旋操作
    2. 平衡二叉树高度为log2,逻辑上很近的节点实际可能非常远,不能有效利用磁盘预读

空间局部性原理:如果一个存储器的某个位置被访问,那么它附近的也会被访问

  • 从磁盘角度来看待B树设计
    1. 索引的效率依赖于磁盘的IO次数,磁盘IO数越少则索引越快。
    2. 索引的原理:不断缩小查找范围,因此B-树每次将范围分割为多个区间,越多越准确
    3. 新建节点时会申请页大小,磁盘IO一次性读取多个block,称为1页

总结:
B树每个节点都有多个值(类似2-3-4树),二叉树一个节点就一个值。B树吧每个节点都给了一点的范围区间,范围区间越多则搜索的越快,例如1100个数,二叉树只能从050和50100中找,但B树能从125,2550,5075,75~100中找,一次性过滤4分3的数据,因此B树更快。

B树是一类树,包括B-树,B+树和B*树,是一颗自平衡的搜索树,它类似普通平衡二叉树,B-树允许每个节点有更多的子节点

B树是专门为外部存储器设计的,如磁盘,他对于读取和写入大块数据有良好性能,所以一般用于文件系统及数据库中

概述

B树也叫Balanced Tree(多路平衡查找树),B-Tree

跟AVL树一样,B树在支节点和叶子节点存储键值、节点引用和数据地址

B树可在内部节点同时存储键和值,因此把频繁访问的数据放在靠近根节点的地方也将大大提高热点数据的查询效率。

所以B树在特定数据重复多次的查询场景更加高效。

image.png

特点
  • 所有键值分布在整个树中,索引值和具体data都在每个节点
  • 任何一个关键字出现且仅出现在一个结点中
  • 搜索有可能在非叶子节点结束(最好O(1)找到数据)
  • 在关键字全集内做一次查找,性能逼近二分查找
B-树的查找

image.png

比如上图中,若搜索 key 为 25 节点的 data,首先在根节点进行二分查找(因为 keys 有序,二分最快),判断 key 25 小于 key 50,所以定位到最左侧的节点,此时进行一次磁盘 IO,将该节点从磁盘读入内存,接着继续进行上述过程,直到找到该 key 为止。

B+树

概念

B+树是B-树的变体,也是一种多路搜索树,它与B-树的区别在于

  • 所有关键子在存储在叶子结点出现,内部节点(飞叶子结点并不存储真正的data)
  • 为所有叶子结点增加了一个链指针

image.png

因为节点不在存储data所以B+树的叶节点和内节点大小不同,为了增加区间访问性一般会对B+树进行一些优化。

image.png

特点
  • 非叶子结点存放元素,只作用索引作用,所有数据保存在叶子节点
  • 所有的叶子节点中包含了全部元素的信息,和含有这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序连接
  • 所有中间节点元素都存在子节点,在子节点元素中是最大或者最小,每层容纳的元素更多,磁盘每一页存放更多的元素,因此磁盘IO的次数减少了
  • B+树更加稳定,因为所有数据都在叶子节点,每个叶子结点通过指针指向构成链表,遍历寻找数据更方便

B-树和B+树的区别

  1. B+树内节点不存储数据,所有data存储在叶节点,查询时间复杂度固定为logN,而B-树查询时间复杂度不固定,与key在树中的位置有关,最好的情况为O(1)

    如下所示B-树/B+树查询节点 key 为 50 的 data

    image.png

    image.png

  2. B+树的叶子结点数据都是使用链表连接起来的,而且在磁盘里是顺序存储的,所以当读到某个值的时候,磁盘预读原理就会提前把这些数据都镀金内存,是的范围查询和排序更快

    B+树可以很好的利用局部性原理,若我们访问节点 key为 50,则 key 为 55、60、62 的节点将来也可能被访问,我们可以利用磁盘预读原理提前将这些数据读入内存,减少了磁盘 IO 的次数。当然B+树也能够很好的完成范围查询。比如查询 key 值在 50-70 之间的节点。

  3. B树的节点都存了key和data,而B+树只有叶子节点存data,非叶子节点只是索引值,没有实际的数据,所以B+树在一次IO中能读取更多的索引值,从而减少查询时的IO次数

image.png

使用B+树的好处

B+树的内部节点只存放键,不存放值,因此一次读取可以在内存页中获取更多的键,有利于更快的缩小查找范围。B+树叶节点由一条链项链,因此当需要进行一次全数据遍历的时候,B+树只需要使用O(logN)找到最小的节点,通过链表O(N)的顺序遍历即可。

而B树需要对树的每一层进行遍历,需要更多的内存置换次数,因此,也就需要花费更多的时间。

MySQL为什么使用B-Tree

红黑树等数据结构也可以实现索引,但因为数据库或文件系统数据量比较大,磁盘无法将所有加载到内存中,当红黑树进行自旋平衡的时候则会出问题,且开销太大,所以文件系统一般是使用B-Tree的数据结构

一般来说,索引本身就很大,不可能全部存储在内存中,所以索引文件的形式来进行存储在磁盘中。所以在索引查找的时候就需要进行磁盘IO,对于内存磁盘IO的读取速度就要慢很多了,所以尽量的降低磁盘的IO次数能提高查询的效率。

数据库为什么使用B+树而不使用B树
  • B树适合做随机检索,而B+树支持随机检索和顺序检索
  • B+树节点只存储索引地址,元素占用空间小,一次内存页读取更多,减少了IO的次数并且符合局部空间原则
  • B+树的查询效率更稳定,B+树都是logN,遍历节点则是O(N)而B树跟key的位置有关系,最小是O(1)
  • B树没有解决元素遍历效率低下的问题,B+树的叶子节点使用指针顺序连接在一起,只需要遍历叶子结点即可实现整棵树的遍历,而且在数据库中范围查询时很频繁的,而B树不支持
  • 增删节点时,B+树的叶子结点包含所有关键字,并以有序的链表结构顺序存储,这样提高了增删的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值