6、Hash Tables
(帮助执行器,去文件页里面读取需要的文件)是k/v结构,k来之后需要用hash函数进行编码。
空间复杂度O(n),时间复杂度O(1),最差O(n)--所有的key都是一样,退化成链表
static hash TABLE:
一个定长的数组,进来的key用最简单的取余或者取模的方式得出一个值作为其存放的地址。保证不管多大的数都能有位置进行存储,前提是一个所有的hash值都是唯一的
设计哈希表的问题:1、hash 函数,哈希函数速度快则容易碰撞 2、原始静态哈希表不能处理碰撞,小表和大表的权衡,小表意味着更加频繁的碰撞,就需要额外的查找和插入操作。
hash Functions:送进去一个数,出来一个数组整数。不要用加密的hash函数(无法反译),常用的函数:
开放地址哈希:
linear probe hashing:碰撞的话,则往后顺延,
但是删除时容易出错,例如删除C之后删除D,hash无法找到D的位置,会以为D已经被删除,解决方法:Tombstone(设置墓碑标记);Movement(对删除之后的数据进行整理)
Non-unique key(重复键):解决方法 1、用指针指向对应值的数据结构
2、redundant key:将值和键拼到一块当成新的键。
robin hood hashing
除了键值之后,后面还带着标志,被记为后推了几位。需要比对【】中的值,不要出现【】中特别大的值。
cuckoo hashing
第一步先插入A,多个表都没有发生碰撞则随机插入一个表即可,第二步插入B,同插入A;第三步,插入C发现和B碰撞了则挤掉B的位置,让B去表1,然后B又把A给挤掉了,再给A找位置,直到最后都不产生碰撞。
动态哈希表结构:
静态的哈希结构的大小是定死的不能进行伸缩,限制较多。
chained hashing(拉链式的哈希):
四个指针,指针指向后面的桶,若后面的桶容量不够则继续往后延伸,一直继续下去。java中如果链表特别长则会转化为红黑树。go中会延伸出溢出桶。hash槽是定的。
extenndible hashing(可扩展的哈希):
标志位为2,则只看前两位,标志位为1则只看一位。若后边的桶满了,则将全局的标志位改为3,后面的进行rehash;每次目录分裂都是两倍的形式,然后再桶分裂(只有溢出的桶需要重新hash)
linear hashing:
split Point指向槽点,当发现1满了之后会从指针指向的点开始进行分家,并添加一个槽位4,然后启用新的hash2函数对0中原有的值进行hash,再将其分配到对应的槽中。而后指针下移。当查找一个数时,也会通过指针的位置判断其是否分家,根据不同的情况来决定启用那个hash函数。后面的也按照这个步骤进行上述操作。
7、B++ Tree
表的索引:原数据表的一个副本,例如抽取公司所有人的年龄抽取出来做一个小表,数据与原数据是同步的。目的是,执行时可以加速执行。
B+树
自平衡的数据结构,可以增删改查,线性遍历,时间复杂度可以控制在O(log n)。
一个父节点有m个子节点,每个节点有m/2 -1 到 m-1个数据,上层只是一个索引,最终的数据都在最下层,可以通过上层的索引进行查找,节点中也是一堆k/v,其中k是我们要索引的值,v的话分为中间节点(下一层孩子的地址)和叶子节点(行id或行地址)
leaf node value
1、Record IDs:行ID 2、k是要索引的列,v就是整个行数据(主键索引的方式)
B树和B+树:B树中间节点可以当节点也能当数据,更加高效。
B++树支持跳跃搜索,
B++树的插入,删除:要找到数据,并观察操作之后的数据,如果太少了就要与其他的数据进行合并;如果插入数据重复了,则在key后面附加行号之类。
聚簇索引是有序的,非聚簇索引遍历要增加顺序方案
B+树节点的大小,磁盘速度越低则b+树的节点尺寸越大越好,最好和文件页的大小一致
节点合并的阈值是需要设定的。
变长的key,处理方案:1、存的是指针 2、 b+树原本的节点就是变长的(但是很少用) 3、添加填充,如果数据不到要求则进行补长 4、存槽,槽指向数据
节点内部搜索:1、线性搜索顺序搜索 2、二分查找 3、推断查找,自主推断规律,按照规律进行
B+树优化
1、前缀压缩
2、去除冗余
3、按批次插入,如果要插入一组树,则先将插入的排成一个B+树,再将这个数放到主树