查找算法,主要是计算平均查找长度?
基本概念
查找是在给定的数据结构中搜索满足条件的结点。
查找也称为检索。
衡量一个查找算法好坏的依据主要是查找过程中 需要执行的平均比较次数,或称为平均查找长度
顺序查找
简单且低效的算法
逐个将每个结点的关键码和待查的关键码值进行比较,直到找出相等的结点或者找遍了所有的结点。
执行顺序查找算法时,被查找的线性表可以是顺序存储或链接存储,对结点没有排序要求。
最好情况下,时间复杂度为O(1)
最坏情况下,时间复杂度为O(n)
一般每个结点都有相同的查找概率,此时顺序查找的平均长度为n/2,时间复杂性为O(n)
折半查找
折半查找,首先找到表的中间结点,将其关键码与给定的要查找的值进行比较,若相等,则查找成功;若大于要查找的值, 则继续在表的前半部分折半查找,否则继续在表的后半部分进行折半查找。
对存储和排序的要求:顺序存储且结点排序
比较判断key中间值的大小,改变查找的范围
最好情况,只需比较一次就找到对应结点,时间复杂度为O(1);
最坏情况,找不到对应结点,需要logn次比较, 时间复杂度为O(logn) ;
平均时间复杂度为O(logn)。
对比
分块查找
以每块最大关键码为值加入索引表,先比较索引表,找到块比较。
如果既要有较快的查找速度,又要满足元素动态变化的要求,可以采用分块查找算法。
分块查找将一个大的线性表划分成若干块(如何分块?),块内不排序,块之间排序(假设非递减)。建立一个索引表,把每块中的最大关键码值作为索引表的关键码值,且非递减排序。
查找某结点时,先在索引表中顺序查找或者折半查找,找到该结点对应的块,然后在块内顺序查找
复杂性分析
分块查找的平均查找长度(比较次数)由对索引表的平均查找长度和对块的平均查找长度组成。设线性表有n个结点,等分成b块,每块 有s=n/b个结点。假设对索引表和块都采用顺序查找,假定对每个结点的查找概率相同。 则平均查找长度为b/2 + s/2 = (n/s+ s)/2 ,当s2 =n时取得最小值O(n0.5)
比较
字符串匹配
串
基本概念
串是字符的有限序列,也称字符串。串是一种线性表,每个结点的数据为一个字符。
串广泛应用于输入输出、文本编辑、信息 搜索等。搜索引擎的核心技术是高效快速 的串匹配算法
串中任意个连续的字符组成的子序列称为该串的子串。空串是任何串的子串。任意串都是其自身的子串。串S的子串中除了其自身外,都是S的真子串
顺序存储是串的最常用的存储方式。 C/C++中,每个字符占用一个字节,最后附加0x00表示字符串的结束。顺序存储的串在删除字符和插入字符的操作时, 都要移动字符
串匹配问题,定义p[a:b]
朴素的串匹配算法
将P中的字符依次与T中的字符进行比较。 设T=t[0:n-1], P=p[0:m-1], m<=n. 从T的最左端开始进行比较
朴素的串匹配算法每趟最多比较 m次,最多n-m+1趟,总的比较次数最多为m(n-m+1),所以 时间复杂度为O(m×n)。
朴素的匹配算法效率低,实际的应用中很少采用
KMP匹配算法
KMP匹配算法是由Knuth,Morris和Pratt 提出的一种快速的串匹配算法。
KMP算法考虑: (1)当匹配失败时,应该将P右移多少个字符;(2)P右移后,应该从P中的哪个字符开始比较。
性质
计算next数组,直接上代码和题目
void getNext(char t[], int next[]){
int i=0,j=-1;
next[0]=-1;
while(i<strlen(t))
{
while(j>=0&&t[i]!=t[j])
{
j=next[j];
}
i++;j++;
if(t[i]==t[j])
next[i]=next[j];
else next[i]=j;
}
}
散列查找
时间复杂度可达到O(1)
散列函数
关键字值转换函数——散列函数,也称哈希函数。将关键字通过函数映射到一个较小区间,再利用映射后的值作为访问结点的下标。
与散列函数关联的是散列表(哈希表),用来存放结点数据或数据的引用。
散列函数经常是多对一,这就必然导致了”冲突“,也称碰撞,具有相同散列值的关键字称为同义词。引入负载因子a=散列表中的结点数目/散列表的长度。a>1,碰撞不可避免。一般要求a<1,但不能太小,否则会造成空间浪费。
除留余数法是最常见的散列函数之一,它将输入值除以一个固定的数,然后取余数作为散列值。
数字分析法是一种针对输入数据中重复模式的散列函数,它将输入数据划分为一个或多个数字,然后根据数字之间的关系来生成散列值。
平方取中法是一种将输入数据求平方后取中间数作为散列值的方法,可以有效地避免数字分布不均的问题。
随机乘数法是一种将输入数据乘以一个随机数后取整作为散列值的方法,可以有效地减少冲突。
折叠法是一种将输入数据分成固定大小的块后相加,再取余数作为散列值的方法,主要用于处理大量数据的情况。
基数转换法是一种将输入数据按照一定进位制进行转换后,再将转换后的数作为散列值的方法,通常被应用于字符串散列。
冲突的处理
开放地址法
如果散列值指定位置已被占用,则依据一定规则在表中寻找其他空闲表项。
开放地址法是一种解决散列查找中冲突的方法,但是他可能会引起同义词合并的问题。当有两个连续的地址0,1都被地址为0的同义词占用了,那么当地址为1的同义词和地址为0的同义词争夺同一个地址2,此时既需要处理同义词的冲突又添加了非同义词的冲突。
探测空闲表项的方法
线性探测法:顺序探测下一个表项
双散列函数探测法:两个散列函数hash1,hash2,正常用hash1计算散列值,当发生冲突时用hash2探测。
链表地址法
为散列表每个表项建立单链表,用于链接同义词子表。
子表的建立有两种方法,一种是分离的同义词子表法,在基本存储区域外开辟新空间存储同义词子表,另一种是结合的同义词子表法,将子表存储在散列表所在的基本存储区域内,指针指向散列表项。