数据结构-拉钩教育

血红的双手。 提交于 2020-08-11 18:05:05

感谢拉钩教育平台,感谢蔡元楠老师以及各位同仁

总的来说很好,有国外一线大厂的使用实例讲解,区块链那快通俗易懂也是我写这篇的缘由之一,但是只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数据合并

  1. 定义大小为N的固定数组segement,当n+1个数据要写入segment结构创建新的segment,n+1个数据写入到新的segment中
  2. 当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声明了一个二维数组,实际上内存并不是连续分配的,而是保存了多个不同的一维数组的首地址

以二维数组为例,数组对象从起始地址开始都保存着每个元素里一维数组的起始地址。如果是三位数组的话,数组对象从起始地址开始都保存着每个元素里二维数组的起始地址,这样递归回去

 

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!