感谢拉钩教育平台,感谢蔡元楠老师以及各位同仁
总的来说很好,有国外一线大厂的使用实例讲解,区块链那快通俗易懂也是我写这篇的缘由之一,但是只18章内容、不可能面面俱到,还需要多方学习,还是很感谢蔡老师的分享
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=20#/detail/pc?id=513
计算机内所有数据结构本质上可归为:数组和链表
数组
一组被保存在连续存储空间中具有相同类型的数据元素集合,内元素可通过自身索引index进行访问
随机访问:用同等时间访问到一组数据中任意一个元素
获取数组元素的方式:
base_address + index * data_size
二维数组
行优先:每一行每个相邻元素保存在相邻的连续内存中
123456 data[i][j]
base_address + data_size *( i * number_of_column + j)
2地址 0x80000000+4*(0 * 3+1) 32位计算机 int占4字节 data_size=4
列优先:每一列每个相邻元素保存在相邻的连续内存中
base_address + data_size × (i + number_of_row × j);这里number_of_row是2
cpu读取内存:cpu缓存策略,cpu读取程序指定地址时:将地址相邻的一些数据一并读取到更高一级的缓存中,L1 L2缓存
位数组
byte字节内存空间基本单位
比特bit 二进制单位
int[2] 4*8 32bit
redis的bitmap:通过string类型表示的(redis中string最大长度512M)
512M=2^29bytes=2^32bits=4294967296 42亿多个状态
bitcount 位数组中有多少比特位是‘1’、 bitfield、bitop与 或 非 异或操作、getbit、setbit(offset)
链表
内存管理器为创建的数组分配相应大小的连续存储空间
链表:非连续存储空间,每个元素里保存到一个元素的地址,元素:节点 头节点head node 尾节点tail node
顺序访问,从第一个元素开始遍历
与数组:
数组大小无法改变、增加元素重新创建新数组so开始创建数组声明多余空间出来方便添加元素:浪费空间
链表元素需要时才创建出来,使用时节点的地址无法使用,利用率 值大小/值大小+节点地址大小,值越大利用率越高
单向链表
节点只保存只想下一节点地址的信息
双向链表前后节点地址都保存了
循环链表:尾节点指向下一节点地址的信息更新为指向头节点
应用:
定时器:计算机时钟 毫微秒nanosecond纳秒 十亿分之一秒
kafka 提供实时处理消息事件的服务,判断发送出去的事件是否被订阅消息的用户收到,大量定时器判断发出的消息是否超时然后重发
purgatory
哈希
本质是数组
当字符串通过计算算出的哈希值大于 2^(32-1 )时,也就是大于 32 位整数所能表达的最大正整数了,则会造成溢出,此时哈希值就变为负数了
hashCode一直使用31正整数进行计算,减少哈希值碰撞、jvm优化(h << 5)-h位运算更快效率
哈希函数一般会有以下三个特性:
- 任何对象作为哈希函数的输入都可以得到一个相应的哈希值;
- 两个相同的对象作为哈希函数的输入,它们总会得到一样的哈希值;
- 两个不同的对象作为哈希函数的输入,它们不一定会得到不同的哈希值。
应用:特性
SHA-1加密算法
git采用SHA-1算法对每一个文件对象进行一次哈希值运算
提交代码,git commit时git将all文件+元数据做SHA-1运算得到新的哈希值;元数据包括上一次commit时的哈希值:避免有人篡改
比特币:
第一次有人讲的这么清楚,区块链我刚才看了一下价格:曾经有一份发财致富的机会摆在我面前、我没有珍惜((꒦_꒦) ε=ε=ε=(#>д<)ノ)
所有记录存放在“区块”的数据结构里(链表数据结构的一个节点),用户新的教育记录打包时自己创建新区块放到整个区块链的结尾,头节点叫做创世区块
哈希值寻找节点,SHA-256加密哈希函数,每个区块计算出一个256位的哈希值,每个新区块中保存上一区块计算出来的哈希值
只有挖矿成功了才有资格去打包交易信息建立新区块
哈希碰撞
开放寻址
数组中找未被使用的位置,新值插入,利用数组原本空间不开辟额外空间保存值
沿着数组索引一个个寻找未被使用的空间:线性探测
平方探测:每次检查空闲位置的步数为平方的倍数,新元素插入的键所产生哈希值i,下一次检测位置 i 加上 1 的平方、i 减去 1 的平方、i 加上 2 的平方、i 减去 2 的平方、…,以此类推
二度哈希:底层保存多个哈希函数,使用第一个函数算出哈希值碰撞了,使用第二个函数算哈希值、类推
分离链接法
将所有哈希值的键值对存在一个链表中,底层数组元素就是存这个哈希值对应的链表
jdk7的hashMap 数组加链表
bloom Filter
哈希表和位数组结合基于概率的数据结构,超大集合中元素是否存在
将元素通过多个哈希函数映射到位数组中,存在误判
m位数组里位的个数、n表示已存储在集合里的元素个数,k标识哈希函数的个数
Memcached
分布式键值对存储系统,多种文件格式:图片 视频等,完全保存在内存
facebook使用名为mcrouter服务器集群将不同数据请求导向不同memcache
用例:
回放:直播视频流数据按每秒分割出块segment、每块存在mc中,读取直播视频流时以直播id加上时间进度 为key 读取每秒直播视频
数组支持随机访问、链表适合较频繁的增删改操作,哈希快速查询和更新、均摊O(1)
树
节点和边连接组成,非线性数据结构
编程实现
基于链表:每个节点类型维护一个子节点指针和指向兄弟节点的链表
插入树节点O(1),siblings链表插入一个元素 或 增加 left_child 节点
基于数组:每个节点维护一个含其所有子节点的数组
父节点实际上维护的是一组子节点指针,子节点内存管理:
- 再实现一个nodePool管理
- 模仿链表内存管理,实际是二叉的链表
树节点的插入:移动其后的all节点,O(n)
删除的话:O(n) 节点的子树拷贝到上层节点
树的遍历
前序遍历:先访问跟节点、递归访问n的子树
后序遍历:递归访问n的子树,后访问跟节点n
按层遍历:从上到下、从左到右
class TreeNode {
TreeNode* left_child;
TreeNode* sibling;
}
void PreorderVisit(TreeNode root) {
Visit(root);
for (TreeNode* child = root->left_child; child != nullptr; child = child->sibling) {
PreorderVisit(child);
}
}
sql语法树
sql解析器parser:将输入的sql解析成sql语法树AST (abstract syntax code)
sql优化器optimizer接受前面解析的原生语法树,优化重写语法树和执行计划
优化器:语法树结合特定用户数据库配置、数据实际分布进行优化
sql执行器执行优化重写的sql语法树
先问个问题:为什么idea 、 系统 会 有保留字 ,让你命名的时候不能肆无忌惮呐?
SELECT name FROM table;//变成树,谢谢
不同服务器RPC间传递树使用S-expression:
(SELECT schema.table.name (FROM schema.table))括号的第一个 token 被定义成这个括号表达的子树的根,每一个嵌套的括号就是一个子树;反序列化也很简单,只需要把每一个括号展开成一个子树就可以了
平衡二叉查找树
二叉树:每个节点至多有2个孩子
二叉查找树:任意节点比其左子树all节点大、比右子树所有节点小,O(h)树的高度
平衡树:形状保持平衡的同时高度得到控制,左右子树深度差<=1
红黑树:二叉树,根节点黑色,其他节点要么红要么黑,红节点不能有红孩子,每条从根节点到底部路径都经过同样数量的黑节点,O(log n)
B树:叶子节点的深度一样,非叶子节点存b-1到2b-1个值,根节点最多存2b-1个值
log-structured
append-only sequence of data写操作添加到数据结构中,不更新原来的值;占用空间越来越大时使用compaction数据合并
- 定义大小为N的固定数组segement,当n+1个数据要写入segment结构创建新的segment,n+1个数据写入到新的segment中
- 当segement到达一定数量时,compaction通后台线程将不同segement合并成compacted segment,满了之后再合并
SSTable sorted string table
在log-structured基础上,保存ls结构里的数据都是键值对,且键必须是字符串,经过compaction后compacted segment里存的键值对都必须是按字符排序
查找单词出现次数,遍历所有compacted segment查看词频
log-structured结构的数据保存到二叉查找树中 ,写入时按任意顺序写入、读取时按二叉查找树中序遍历访问数据 相当于字符串顺序读取,这个平衡树叫memtable,通过内部维护平衡树进行compaction优化:LSM树
hbase sqlite MongoDB elasticsearch solr 底层均运用了LSM树,搜索引擎还加了bloom filter判断词是否在数据中
树优化数据查询效率
图
一个集合G=(V,A),V图节点集合,A节点间连接的边,有向和无向
与树:
图和树所抽象的数据关系不一样,树表达层级化的结构,图表达网络化的结构
实现方式
邻接矩阵法
超大数组,数组中间元素的true/false来表达边
V个节点,需要V*V大小的数组
2、邻接链表法
写‘法’的时候脑子里蹦出‘大不赦’的词,小时候被不纯洁的纸币留下不好的印象
存储每个节点所指向的点,如图
很简洁不多写了
邻接矩阵访问快占用空间适合边比较多的图,不管多少边需要一样的内存
邻接链表更快操作相邻节点,稀疏图中边比较少、链表短、效率高
spark有向无环图DAG
https://www.cnblogs.com/Evsward/p/dag.html
利用有向无环图表达数据处理后可对数据处理流程做自动优化;计算资源的自动弹性分配
兄弟融合优化 sibling fusion
拓扑排序
对有向无环图,排序all节点,使得从节点u到节点v的每个有向边uv,u排序都在v前
uber拼车:贪心算法局部最优,司机接驾过程中继续进行匹配,找到更优,切换司机,确定了拼车路线后利用这条路线尝试接受新的拼客
应用
LRU缓 lease recently used 最近最少使用
get set remove 哈希表,时间复杂度O(1),但是不知道数据插入的顺序
链表:头节点直线缓存中最新数据,尾最老的,新的插入前面;查找O(n)
哈希和链表组合:哈希值存链表节点位置,链表元素来回移动、双向链表
类linkedHashMap,核心实现同上的组合
Trie树
前缀树或字典树
一致性哈希算法
计算出来的哈希值映射到环中
一般据ip地址计算分布式环境下机器的哈希值,将机器映射到圆环上
计算数据哈希值,据值映射到圆环上,并将其存放到顺时针方向上最接近它的机器
一般在Java声明了一个二维数组,实际上内存并不是连续分配的,而是保存了多个不同的一维数组的首地址
以二维数组为例,数组对象从起始地址开始都保存着每个元素里一维数组的起始地址。如果是三位数组的话,数组对象从起始地址开始都保存着每个元素里二维数组的起始地址,这样递归回去
来源:oschina
链接:https://my.oschina.net/u/4342648/blog/4479695