diff --git a/.DS_Store b/.DS_Store index 5e5996a..ca2312a 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/数据结构/数据结构和算法.md b/数据结构/数据结构和算法.md index 0cb2ae7..0789986 100644 --- a/数据结构/数据结构和算法.md +++ b/数据结构/数据结构和算法.md @@ -911,43 +911,326 @@ ### 基本概念 -### 顺序查找 +- 查找:在数据集合中寻找满足某种条件的数据元素的过程 +- 查找表(查找结构):用于查找的数据集合 -- 一般线性表 -- 有序表 + - 数据元素(记录)的类型相同 + - 可以是一个数组或者链表等数据类型 + - 常见操作 -### 折半查找 + - 查询某个特定元素是否存在 + - 检索满足条件的特定元素的各种属性 + - 数据元素插入 + - 数据元素删除 -### 分块查找 +- 静态查找表:不需要动态地修改查找表,与动态查找表对应 -### B树和B+树 + - 顺序查找 + - 折半查找 + - 散列查找 + - .... + +- 动态查找表:需要动态地插入或者删除的查找表 + + - 二叉排序树的查找 + - 散列查找 + - .... + +- 关键字:数据元素中唯一标识该元素的某个数据项的值 + + - 使用关键字查找,查找结果应该是【唯一的】 + - 可以类比集合中不重复的key + - 是不是想起了数据库的主键的概念???? + +- 平均查找长度:所有查找过程中进行关键字的比较次数的平均值 + + - 查找长度:一次查找需要比较的关键字次数 + - 【是衡量查找算法效率的最主要的指标】 + +### 顺序查找(线性查找) + +- 主要用于在线性表中进行查找 + + - 一般的【无序线性表】的顺序查找 + - 按关键字【有序的顺序表】的顺序查找 + - Tips:顺序表是指顺序存储的线性表,前面的有序才强调元素有序,顺序强调的是存储方式 + +- 基于一般线性表的顺序查找 + + - 最直观的查找方式 + - 基本思想:从线性表的一端开始,逐个查询条件和关键字进行比对即可 + - 【重要】:哨兵的引入,可以避免很多不必要的判断语句,提高程序的效率 + - 平均查找长度 + + - 查找成功 + + - 第i个元素需要进行(n-i+1)次关键字比较 + - 概率相等的情况下:(n+1)/2 + + - 查找失败【没有找到元素】 + + - 各关键字比较次数:(n+1) + - 平均查找长度:(n+1) + + - 综合分析 + + - 【缺点】:n较大时,平均查找长度较大,效率低 + - 【优点】:对数据元素的存储没有要求,顺序存储和链式存储都可行。对数据元素的有序性也没有要求 + - 【注意】:线性的链表只能进行顺序查找 + + - 通常情况下,查找表中记录的查找概率并不相等 + +- 基于有序表的顺序查找 + + - 预先已经知道表是按照关键字有序的,基于【二叉判定树】来查找,不用全部遍历----->【降低顺序查找失败的平均查找长度】 + - 平均查找长度 + + - 查找成功 + + - 和一般线性表的顺序查找一样 + - 概率相等的情况下:(n+1)/2 + + - 查找失败 + + - 到达是失败结点所查找的长度=失败结点父结点所在的层数【建议画画二叉判定树】 + - 查找概率相等时,平均查找长度:n/2 + n/(n+1) + + - 【注意】:有序表的顺序查找中的线性表可以是链式存储结构的 + +### 折半查找(二分查找) + +- 【注意】仅仅适用于有序的顺序表 +- 基本思想:首先与表中间位置元素的关键字比较,相等则查找成功,不相等则向左|向右继续与该部分中间元素比较...... +- 核心思路:左右双指针,互相往中间靠拢,可以用二叉判定树来辅助思考 +- 平均查找长度【计算非常重要】 + + - 查找成功 + + - 折半查找法查到给定值的比较次数最多不会超过【判定树】的高度 + - 相等概率下,平均查找长度约等于log2(n+1) -1 ,【建议结合二叉树高度计算来理解】 + + - 查找失败直接看例题..... + +- 综合分析 + + - 折半查找的时间复杂度为O(log2n), 平均情况下笔顺序查找O(n)的效率高 + - 折半查找需要方便地定位查找区域---->存储结构必须具有【随机存取】的特性 + - 【注意】仅仅适用于有序的顺序表,不适用链式存储结构,要求表按关键字有序排列 + +### 分块查找(索引顺序查找) + +- 具备顺序查找和折半查找的优点,既有动态结构,又能适用于快速查找 +- 是不是有点【希尔排序】和【直接插入排序、折半插入排序】的发展效果????? +- 基本思想:查找表分成若干个子块 + + - 块内元素可以无序,【块之间必须有序】 + - 前一块的最大关键字永远小于后一块的最小关键字 + - 【重要】:建立索引表,包含每个分块额最大关键字和第一个元素的地址(起始角标) + - 索引表按关键字有序排列 + +- 查找方式 + + - 索引表:顺序查找或者折半查找 + - 块内:顺序查找<----- 块内可以无序 + +- 【直接看书】平均查找长度=索引查找平均长度+块内查找的平均长度 + +### 【直接看书|视频,这部分我很迷糊】B树和B+树 + +- B树(多路平衡查找树) + + - 基础概念 + + - 阶:所有结点的孩子结点数的最大值 + - m阶B树(可能为空树)满足 + + - 树中每个结点至多有m棵子树(即至多包含m-1个关键字) + - 如果根结点不是终端结点,至少有两棵子树 + - 所有的叶结点都出现在同一个层次上,不带任何信息 + - .... + + - B树是所有结点的平衡因子均为0的多路查找树 + + - 基本操作 + + - 高度 + + - B树的大部分操作需要的磁盘存取次数和B树的高度成正比 + - B树的高度不包括最后的不带任何信息的叶结点所处的那一层【有的参考书,也包含这部分】 + - 【重要】如果每个结点重点额关键字个数达到最少,则容纳同样多关键字的B树的高度可以达到最大 + - .... + + - 查找 + + - 步骤 + + - 在B树中找结点【磁盘中进行】 + - 在结点内找关键字【内存中进行】----> 采用【顺序查找法】或【折半查找法】 + - 【Tips】B树常存储在磁盘上,在磁盘上找到目标节点后,将结点中的信息读入到内存 + + - 当查找到叶子结点,对应的指针为空指针,即树中没有对应的关键字 + + - 插入 + + - 插入操作比查找操作复杂多 + - 步骤 + + - 定位 + + - B树查找算法--->最底层中某个非叶结点 + - 【重要】B树的插入关键字一定是插入到最底层的某个非叶结点内 + + - 插入 + + - 插入结点关键字个数小于m----> 直接插入 + - 插入后检查插入结点内关键字个数,【大于m-1必须进行分裂】 + + - 【直接看书】】注意分裂的方法 + + - 删除(删除的关键字在终端结点) + + - 直接删除关键字 + - 兄弟够借 + - 兄弟不够借 -- B树的概念 -- B树的操作 - B+树的概念 + - B+树是根据数据库的需要需要的一中B树的变形树 + - m阶的B+树需要满足的条件 + + - 每个分支结点最多有m棵子树(子结点) + - 结点的字树个数与关键字相等 + - 非叶、根结点至少有两棵子树,其他分支结点至少有m/2(向上取整)棵子树 + - 所有叶结点包含全部关键字及指向相应记录的指针 + + - 叶结点按关键字大小排列 + - 相邻结点按大小顺序相互链接起来 + + - 【反复理解】所有的分支结点(理解为索引的索引)中仅仅包含它的各个子结点(下一级的索引快)中关键字的最大值和指向其子结点的指针 + - .... + ### 散列(Hash)表 - 基本概念 -- 构造方法 + + - 散列函数:一个把查找表中的关键字映射成该关键字对应的地址(数组下标、索引、内存地址等)的函数 + - 散列冲突:散列函数把两个或者多个不同关键字映射到同一个地址上,【冲突总是不可避免的】 + - 同义词:发生散列冲突(碰撞)的不同关键字 + - 散列表:根据关键字直接访问的数据结构,是关键字和存储地址之间的一种直接映射关系 + - 理想情况下,散列表中查找时间复杂度为O(1),与元素个数无关 + - 基于【比较】的查找算法,查找效率取决于比较的次数 + - 以上可以结合哈希加密、布隆过滤器等工作中的编程概念去比对学习.... + +- 散列函数 + + - 构造散列函数,需要注意 + + - 散列函数的定义域【必须】要包含全部需要存储的关键字,值域的范围则依赖于散列表的大小或地址范围 + - 散列函数计算出来的地址应该能【等概率、均匀的】分布在整个存储空间,尽可能减少散列冲突---> 【压测】 + - 散列函数应该尽量简单,能够在较短时间内就计算出任一关键字对应的散列地址 + + - 常用散列函数 + + - 直接定址法 + + - 直接去关键字的某个【线性函数值】为散列函数 + - 优点 + + - 计算简单 + - 【不会产生冲突】 + - 适合关键字的分布【基本连续】的情况 + + - 缺点:关键字分布不连续时,空位较多,将造成存储空间的浪费 + + - 除留余数法 + + - 最简单、最常用的散列方法 + - 关键在于【选择被除数P】,等概率进行映射,尽可能减少冲突的可能性 + + - 数字分析法 + + - 适用于已知的关键字集合 + - 如果更换了关键字,就需要重新构造新的散列函数 + + - 平方取中法 + + - 取关键字的平方值的中间几位来作为散列地址 + - 适用于关键字的每一位取值都不够均匀或小于散列地址所需要的位数 + + - 折叠法 + + - 将关键字分割成位数相同的几部分,取这几部分的叠加作为散列地址 + - 关键字位数很多,每一位上数字分布大致均匀时,可以采用折叠法得到散列地址 + + - ..... + + - 不同的情况,不同的散列函数会发挥不同的性能,无法笼统的说那种散列函数最好。散列函数的目标都是为了将产生冲突的可能性尽可能地降低 + - 处理冲突 - - 开放定址法 + - 【任何设计出来的散列函数都不可能绝对地避免冲突】,必须考虑在发生冲突时如何进行处理 + - 处理方法 - - 线性探测法 - - 平方探测法 - - 再散列法 - - 伪随机序列法 + - 开放定址法 - - 拉链(链接)法 + - 概念:可以存放新表项的空闲地址,既向它的同义词表项开放,也向它的非同义词表项开放 + - 增量取值方法 + + - 线性探测法 + - 平方探测法 + - 再散列法(双散列法) + - 伪随机序列法 + + - 注意事项 + + - 不能随便【物理删除】表中已有元素,删除将会截断具有相同散列地址元素的查找地址,牵涉元素广 + - 删除元素需要【单独做标记】,采用【逻辑删除】 + - 逻辑删除会导致散列表表面上看上满的,实际上很多位置都是没有被利用的,【需要定期维护,将删除标记的元素物理删除】 + + - 拉链(链接)法 + + - 概念:为避免非同义词发生冲突,可以把所有的同义词存储在一个线性链表中【由散列地址唯一标识】 + - 【重要】适用于经常进行插入和删除的情况 + + - 拉链法处理冲突时不存在聚集现象,用线性探测法处理冲突时容易产生聚集现象 -- 散列查找 - 性能分析 + - 装填因子:一个表的装满程度 =(表中的记录数)/ (散列表的长度) + - 查找效率取决于 + + - 散列函数 + - 处理冲突的方法 + - 【重要】装填因子 + + - 散列表的平均查找长度依赖于【散列表的填装因子】 + - 填装因子越大,表示装填的越“满”,发生冲突的可能性就越大 + - 散列冲突导致散列表在查找过程中也是需要进行比较的。【查找效率仍然用平均查找长度来度量】 + - 冲突的产生概率与装填因子(表中记录数和表长)的大小成正比 + ### 模式匹配(字符串) - 简单模式匹配 -- KMP算法 + + - 概念:第一个字符串(模式串)在第二串(主串)中的位置 + - 基本过程:从主串指定字符开始(一般第一个)和模式串的第一个字符逐个比较.... + - 时间复杂度:O(n*m),n、m分别为主串和模式串的长度 + +- 【难点,直接看代码理解】KMP算法 + + - 【重要】是对简单模式匹配的改造,时间复杂度在O(n+m)的数量集上 + - 【建议手动模拟】基本过程 + + - 整个匹配过程,没有进行指针回溯 + - 每趟比较过程让子串向后滑动到一个合适位置, 让这个位置上的字符和主串中的那个字符比较 + - 【重要】next数组的求解实际是对某个位置找到最长的公共前缀 + +- 总结比较 + + - 简单模式匹配时间复杂度:O(n*m) + - KMP算法的时间复杂度为O(n+m) + - 一般情况下,简单模式匹配的实际执行时间可以近似到O(n+m) + - 【重要】KMP算法的重要特点是主串指针不回溯 ## 排序