大话数据结构6 - 查找

来源:互联网 发布:淘宝卖虚拟物品流程 编辑:程序博客网 时间:2024/06/05 23:03
查找searching
查找:根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素/记录
查找表,查找集合:同一类型数据元素/记录的集合
查找结构:专门为查找操作设置的数据结构,表、树、图结构
查找结果:成功(给出整个记录的信息,给出该记录的位置),不成功(给出一个空记录,给出一个空指针)
按操作方式分类:2类
1、静态查找表:只作查找操作1.查询某个“特定”数据元素是否存在,2.检索某个“特定”数据元素及数据项信息
2、动态查找表:1.查找时插入数据元素,2.查找时删除数据元素
注意:动态查找表,既要考虑插入、删除操作的效率,又要考虑查找操作的效率

关键字,键值:是数据元素中某个数据项的,用它可以标识一个数据元素
关键码:关键字所标识的数据元素的某个数据项
主关键字,主关键码:主关键字,唯一标识一个数据元素;主关键码,主关键字所在的数据项
次关键字,次关键码:次关键字,不唯一标识;次关键码,次关键字所在的数据项
注意:对不同记录,主关键字均不相同
注意:理解“唯一标识”,“不唯一标识”的含义
这里的“唯一”,指的是对同一字段中的值进行的判断,如果是不同字段,相等、不相等,一般都没有什么实际的意义。“唯一标识”在同一字段中没有重复值,“不唯一标识”在同一字段中有重复字段,如果所有字段都重复就是重复记录。

顺序表查找,有序表查找,线性索引查找,二叉排序树,平衡二叉树,多路查找树,散列表、哈希表查找

一:静态
1、顺序表查找,顺序表:线性表
从头到尾、从尾到头查找
优化:在查找方向的尽头放置“哨兵”,判断查找是否越界的小技巧
顺序表存放规则:根据查找频率,将经常要查找的记录放在前面,不常用的记录放在后边
2、有序表查找,有序表:a.线性表,b.关键码有序,c.顺序存储
按照分割点选择方法的不同,介绍3种:折半查找,插值查找,斐波那契查找
查找表a,分割点mid,最低下标low=1、记录首位,最高下标high=n、记录末位
1.折半查找,二分查找,mid=(low+high)/2 = low+(high-low)*1/2
2.插值查找,插值公式=(key-a[low])/(a[high]-a[low]),mid=low+(high-low)*(key-a[low])/(a[high]-a[low]),适用:关键字分布比较均匀的查找表
3.斐波那契查找,黄金分割,mid=low+F[k-1]-1,斐波那契数列:F[K]=F[K-1]+F[K-2]
3、线性索引查找,索引表:a.线性表,b.带指针,c.有序

注意:a.索引是对于按先后顺序存储的海量数据,为了加快查找速度而设计的一种查找结构,b.索引是根据原数据集新生成的一个数据集合
注意:索引技术被广泛的用于文件检索、数据库、搜索引擎等技术领域
索引:把一个关键字与它对应的记录相关联的过程,一个索引由若干个索引项组成
索引项:至少包含关键字、对应记录的位置(指针)等信息
按结构分类:线性索引,树形索引,多级索引
这里只介绍线性索引:3种,稠密、分块、倒排
线性索引,索引表:将索引项集合组织成线性结构
1.稠密索引:是指在线性索引中,数据集中的每个记录对应一个索引项
特点:记录无序,索引表中的索引项(按关键字)有序
索引项包含2个数据项:a.关键字,b.指针
缺点:索引项个数与数据集中的记录个数相同,空间代价大
2.分块索引:把数据集的记录按分块有序的原则分成若干块,每一块对应一个索引项
特点:分块有序:a.块内无序,块内记录可以无序,b.块间有序,块与块之间按关键字有序,例如,第一块中的所有记录都小于第二块中的记录等
索引项包含3个数据项:a.最大关键字,b.快中的记录个数,c.指向块首数据元素的指针
最佳情况:分块数m=块中记录数t
3.倒排索引,最基础的搜索技术,根据属性(字段、次关键码)的值来查找记录,不是由记录确定属性值,而是由属性值确定记录的位置
索引项通用结构,包含2个数据项:a.次关键字,b.记录号表:是指向该记录的指针、是该记录的主关键字
优点:查找记录非常快
缺点:记录表号不定长,维护(插入删除等)比较困难

二:动态:既要考虑插入、删除的效率(链式存储),又要考虑查找的效率(有序)
注意:二叉排序树、平衡二叉排序树是动态查找最重要的数据结构,B树是为内外存的数据交互专门设计准备的数据结构
1、二叉排序树,二叉查找树,BST:空树、左<根<右的二叉树,中序遍历的结果是一个从小到大的有序序列
注意:区别有序树(各结点位置有次序、不能互换),二叉树就是有序树
算法:使用二叉链表,递归原理
查找操作,递归查找
插入操作,构建一个二叉排序树
删除操作,分2种情况:a.仅有左子树/左叶子的结点或仅有右子树/右叶子的结点,c.左右子树/左右叶子都有的结点
注意:对于b情况更好的思路是,使用删除结点p的前驱s1或后继s2来替换该结点,然后再删除s。其中前驱s1一定是左子树中最右的叶子或最右的仅带一个左叶子的子树根,后继s2一定是右子树中最左的叶子或最左的仅带一个右叶子的子树根
注意:1.查找操作要在查找遍历整个二叉树后、确定没有该元素、再插入,程序是:插入程序中调用查找函数,2.删除操作是只要找到该元素就可以执行、不用遍历整个树,程序是:a.可以在删除程序中调用查找函数b.也可以在查找程序中调用删除函数
总结:1.插入删除性能取决于存储结构,链式存储,修改指针,2.查找性能取决于二叉排序树的层数/深度:最好情况,深度与完全二叉树相同(平衡状态)、近似折半查找;最坏情况,斜树(二叉树深度等于结点个数)、等同顺序查找
2、平衡二叉树,AVL树:a.是二叉排序树,b.每个结点的左右子树高度差至多等于1
注意:是一种高度平衡的二叉排序树,高度平衡是指:1.要么是空树,2.要么它的左右子树都是平衡树,且左右子树的深度差的绝对值不超过1
平衡因子BF:二叉树上结点的左子树深度减去右子树深度的值,即BF=左深度-右深度,BF=-1、0、1
注意:只要二叉树上有一个结点的平衡因子的绝对值大于1,该二叉树就不是平衡的
最小不平衡子树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树
构建AVL树的思想:在构建二叉排序树的过程中,每插入一个结点就检查是否因新插入的结点而破坏了平衡性,若是,找出最小不平衡子树,进行调整
注意:把不平衡消灭在最早时刻
调整思路:
①BF=正(0、1、2),以BF=1为轴,右旋转/顺时针旋转,得到(0、0、0)
②BF=负(0、-1、-2),以BF=-1为轴,左旋转/逆时针旋转,得到(0、0、0)
③BF=一正一负(0、1、-2),先以BF=1为轴右旋转,得到(0、-1、-2),再②,得到(0、0、0)
④BF=一负一正(0、-1、2),先以BF=-1为轴左旋转,得到(0、1、2),再①,得到(0、0、0)
注意:0=0 1=0 -1
算法:使用二叉链表
①增加bf,存储平衡因子
②右旋函数R_Rotate:(0、1、2),左旋函数L_Rotate:(0、-1、-2)
平衡函数RightBalance:左(0、-1、-2),右左(0、1、-2),平衡函数LeftBalance:右(0、1、2),左右(0、-1、2
④主函数
3、多路查找树:a.每个结点的孩子数可以多于2个,且每个结点处可以存储多个元素,b.所有元素之间存在某种特定的排序关系
注意:多路树,打破了一个结点只存储一个元素的限制
4种特殊形式:2-3树、2-3-4树、B树、B+树
注意:1.其中它们的插入、删除操作比较复杂,2.所有叶子结点都在同一层次上
①2-3树:2结点或3结点
②2-3-4树:2结点或3结点或4结点
2结点:1个元素2个孩子,要么没有孩子要么有2个孩子,不能只有一个孩子,左<右
3结点:2个元素3个孩子,要么没有孩子要么有3个孩子,左<中<右
4结点:3个元素4个孩子,要么没有孩子要么有4个孩子,左<左中<右中<右
③B树:是平衡的多路查找树,2-3、2-3-4是B树的特例
B树的阶:结点最大的孩子数目,例如2-3树是3阶B树、2-3-4树是4阶B树
B树的查找过程:指针查找结点+结点中查找关键字
注意:B树的数据结构是为内外存的数据交互专门设计准备的,适用于外查找的树
注意:内外存的查找性能更多取决于读取次数,所以设计要考虑B树的平衡和层次
④B+树:应文件系统所需而出现的一种B树的变形,解决了所有元素遍历等基本问题
适用:B+树结构特别适合带有范围的查找
注意:前面所讲的树都是没有重复值的,即每一个元素在该树中只出现一次,而B+树不同于之前所讲的树,在B+树结点中的元素会在叶子中再次列出,即元素会重复出现

三:散列表、哈希表查找:静态查找,散列主要是面向查找的存储结构
不用比较,直接通过关键字key得到要查找记录的存储位置
散列技术:在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key),存储位置=f(关键字)
散列函数、哈希(Hash)函数:f,注意:散列技术中最关键的是设计一个简单、均匀、利用率高的散列函数
散列表、哈希表(Hash table):使用散列技术将记录存储在一块连续的存储空间中,该存储空间称为散列表,动态数组
散列地址:记录的存储位置/地址
散列过程:存储、查找
注意:散列技术既是存储方法,也是查找方法
注意:与之前的数据结构(线性表、树、图)不同,散列技术的记录之间不存在什么逻辑关系
注意:与之前不同,之前都是通过不断的比较查找出i,散列是通过公式计算出i,散列表查找非常高效
优缺点:散列表查找直接一步到位、非常高效,但弊端是记录之间没有任何关联,所以对于查找性能要求高,且记录之间关系无要求的数据非常使用
适合:查找与给定值相等的记录,且是主关键字的查找
不适合:次关键字的查找,范围查找,排序查找
冲突:关键字key1≠key2,但f(key1)=f(key2),其中key1、key2称为这个散列函数的同义词
注意:冲突不能完全避免
1、构造散列函数:
原则:a.计算简单,b.散列地址分布均匀(保证存储空间的有效利用,减少为处理冲突而耗费的时间)
①直接定址法
取关键字的某个线性函数,f(key)=a*key+b;优点:简单、均匀、不会产生冲突;适合:事先知道关键字的分布,查找表较小且连续;不常用
②数字分析法
抽取,使用关键字的一部分来计算存储位置;适合:事先知道关键字的分布且关键字若干位的分布较均匀,关键字位数较多
③平方取中法
先(关键字^2)再抽取中间位;适合:不知道关键字分布,关键字位数较少
④折叠法
适合:不知道关键字分布,关键字位数较多
除留余数法
散列表长m,f(key)=key MOD p,(p≦m),其中可以对关键字取模,也可以在折叠、平方取中后再取模;缺点:p值取的不好,很容易有冲突、出现同义词,取的好也不容易避免冲突;最常用
⑥随机数法
f(key)=random(key);适合:关键字长度不等
注意:如何选择以上方法,考虑因素:散列地址计算时间、散列表长度、关键字长度、关键字分布、记录查找频率
2、处理散列冲突
注意:1、2、4都是冲突换址,3是链表
①开放定址法,探测法
一旦发生冲突,就寻找下一个空的散列地址;fi(key)=(f(key)+di) MOD m,di=1,2,...,m-1,单向寻找,线性探测法
堆积:本来不是同义词却需要争夺一个地址的现象
改进,di=1^2,-1^2,2^2,-2^2,...,q^2,-q^2,(q≦m/2),双向寻找,二次探测法
伪随机数,随机种子,di=random(di),随机探测法
注意:只要散列表没有被填满,总能找到不发生冲突的地址
②再散列函数法
fi(key)=RHi(key),i=1,2,...,k,RHi是不同的散列函数,一旦发生冲突就换一个散列函数
优点:使得关键字不会产生聚集
缺点:增加了计算时间
③链地址法
有冲突的关键字存储在一个单链表中,同义词子表
优点:冲突较多时,不会找不到空地址
缺点:查找时可能需要遍历单链表
④公共溢出区法
有冲突的关键字都放在公共溢出表,散列表=基本表+溢出表
查找:先通过散列函数得到散列地址在基本表中找,如果没有,再到溢出表中顺序找
适合:冲突较少的情况
3、散列表的查找
散列表的查找性能取决于:①关键字的分布,②散列函数的选择,③处理冲突的方法,④装填因子α
装填因子=记录个数/散列表长度,α=n/m,通常将散列表的空间设置的比查找表/集合大,空间换时间
注意:不管n有多大,我们总能选择一个合适的装填因子,将平均查找长度限定在一个范围之内

0 0
原创粉丝点击