查找算法

查找算法
查找算法的好坏直接影响一个系统的效率
静态查找:数据集合稳定,不需要添加,删除元素的查找操作
动态查找:数据集合在查找的过程中需要同时添加或删除元素的查找操作

查找结构
对于静态查找,用线性表结构组织数据,可使用顺序查找算法,如果对关键字进行排序,可以使用折半查找算法或斐波那契查找算法,来提高查找效率。
对于动态查找来说,使用二叉排序树的查找技术,散列表结构来解决。

顺序查找最基础,很暴力的一个个对比,略
可设置一个哨兵,可以将复杂度减到O(n)

插值查找
按比例查找,根据折半查找改进,复杂度O(log2n)
很多时候我们不会固定要一半一半的找,而是按比例,比如查字典的B开头单词,一定不会一来就从G开始,而是在前部分找。
根据这个原理,我们可以修改每一次查找的比例

斐波那契算法(黄金比例)
斐波那契数列
1,1,2,3,5,8,13,21,34,55,89…
1+1=2. 1+2=3. 相邻两数之商越来越接近0.618

线性查找算法
海量数据查找,需要建立索引
稠密索引,应用于数据量不是特别大的,因为每一个都有索引,很占内存。
分块索引,块和块之间有顺序,但是每一块只存放最大关键字和起始关键字。所以块内无序。可以增加一个索引记录个数
倒排索引,应用于搜索引擎。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引。

二叉排序树
无序的顺序存储结构查找效率低
有两全的算法是二叉排序树
下图的数列生成二叉排序树,一个个依次和70比较大小,左小右大,增加叶子结点。
在这里插入图片描述
特征
若左子树不空,则左子树上所有结点的值均小于它的根结构的值;
若右子树不空,则右子树上所有结点的值均大于它的根结构的值;
左右子树也分别为二叉排序树(递归思想的应用)

二叉排序的查找
按照中序排列,可以得到一个从小到大的排序
二叉排序的删除
不能因为删除了一个结点丢失了二叉排序树的特性
要删点为叶子节点直接删除
要删点只有左子树或者右子树就子承父业,接双亲
要删点同时又左右子树。如图删105
在这里插入图片描述
由于中序遍历可从小到大排序,所以可以替代105的就是它的前趋和后继,不用更改链接,直接替换数值105为104,103顶到104的位置,104是105左子树最大的,最右的;当然你也可以选择105右子树最小的,最左的,108代替删除的105。

平衡二叉树
之前说的二叉排序树靠运气,因为同样的序列,生成的二叉排序树不同,有时候倒霉可能很耗时间,差别很大
最好的方法就是构建成一棵平衡二叉排序树(AVL树)
要求每个结点的左子树和右子树高度最多相差1,要么为空,要么左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值<=1,0或者1

如果平衡值遭到破坏,那么应该马上做出修正
平衡二叉树的实现

这里转载一篇写的很好的文章分步图解平衡二叉树的插入过程(Python实现)

多路查找树
为了减低对外部储存结构的访问次数,需要新的数据结构。
特点
每一个结点的孩子数可以多于两个,且每一个结点处可以存储多个元素。所有元素之间存在某种特定的排序关系。
多路查找树的每一个结点都具有2或者3个孩子,为2-3树
如下图,注意2-3树所有叶子都在同一层级上。
在这里插入图片描述
2-3树的插入原理

2-3树的删除原理
以上两个视频方便跟着老师做一遍加深印象,但是规律总结的一般般。
推荐看2-3查找树的插入与删除,这个是一个方法总结博客,我觉得还不错,有图有字的。

2-3-4树
同理,4结点要么有4个孩子,要么没有孩子。4个孩子从小到大分布。
构造2-3-4树,了解一下,插入删除道理都一样

B树
一种平衡多路查找树,2-3树和2-3-4树都是B树的特例。结点最大的孩子树数目称为B树的阶,2-3树是3阶B树,2-3-4树是4阶B树。

一个m阶的B树具有如下属性:
若根节点不是叶结点,则至少有2棵子树
每一个非根的分支结点都有k-1个元素(关键字)和k个孩子,其中k满足:m/2<= k <=m,k向上取整
所有叶子结点都位于同一层次
每个分支结点包含下列信息数据:
n, A0,K1,A1,K2,A2,K3,A3… 其中K为关键字,Ki<Ki+1,Ai为指向子树的很结点的指针,第一个空,n,表示这个结点有多少个关键字。
下图为一个B树
在这里插入图片描述散列表(哈希表)查找
之前说的找关键字的方法
顺序表查找:挨个儿比较
有序表查找:二分法查找
散列表查找:纪录得存储位置=f(关键字)

散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每一个关键字key对应一个存储位置f(key)。
f(key)又称为Hash函数。这块连续的存储空间是哈希表

存储记录-通过散列函数计算出记录的散列地址
查找记录-通过散列函数计算出记录的散列地址访问该记录

不需要进行任何比较和迭代,散列表就是一对一的映射,不同关键字通过散列函数计算出的函数不能是相同的,否则就会产生冲突

散列的缺点
不适合查找多个相同关键字或者查找特定范围

迭代和递归得区别
递归是一个树结构
迭代是一个环结构

散列函数的构造
函数计算简单+地址分布均匀=好的散列函数
直接定址法:取关键字的某个线性函数数值为散列地址
f(key)=a*key+b

数字分析法:抽取一部分数字段,比如手机尾号什么的
在这里插入图片描述
平方取中法:将关键字平方之后取中间若干位数字作为散列地址。不适合太大的数,平方计算效率低

折叠法:将关键字从左到右分割成位数相等的几部分,然后将这几部分叠加求和,并按散列表表长取后几位作为散列地址。适合事前不知道关键字的分布,但是直到关键字位数且比较多的情况

除留余数法:很常用的构造法,对散列表长为m的散列函数计算公式为:-f(key)=key mod p(p<=m), 这个方法不仅可对关键字直接取模,也可以通过折叠,平方之后再取模。余数为多少就存在对应数字的下标里,p的选择是关键,要尽量避免余数都一样

随机数法:选择一个随机数,取关键字的随机函数值为散列地址。
f(key)= random(key), random为随机函数,适合关键字常数不相等的情况

不管是字符串还是数字符号,在CPU看来都是ASCII码。
现实中,视不同情况采用不同的散列函数
按大概的重要大小排列参考方向:
计算散列地址的时间
关键字长度
散列表大小
关键字分布情况
记录查找频率

处理散列冲突的方法
开放地址法:一旦发生冲突,就寻找下一个空的散列地址,只要表够大。空地址总能找到。
公式:在这里插入图片描述
还是采用的除留余数法,di从小到大的试,直到找到空位,有时候找了好几次都碰到冲突,就是找不到合适的空位,导致有几个关键字都对应一个下标,但是有几个空着没有用上,这叫堆积现象。
堆积的解决方法:
可以修改di的取值方式,比如对d做正负平方运算在这里插入图片描述
还有,对于位移量di采用随机函数计算,称为随机探测法
在这里插入图片描述
再散列函数法:就是再构造依次散列函数在这里插入图片描述
RHi是你准备的各种散列函数,如果试完了都还不行。。。
那就用链地址法
设计一个链表,不存放关键字,而存放地址,就不怕有冲突了
在这里插入图片描述
最后,公共溢出区法
建立一个溢出表,存放发生了冲突的那一部分,这种方法比较常见

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值