一.查找概论
1.一些概念
查找表:同一类型的数据元素构成的集合
关键字:数据元素中某个数据项的值,又称为键值,用它可以标识(不一定唯一)一个数据元素。也可以表示一个记录的某个数据项(字段),我们称它为关键码
主关键字:若此关键字能唯一的标识一个记录,则称此关键字为主关键字,这也就意味着,对不同的记录,其主关键字不相同。主关键字所在的数据项成为主关键码
次关键字:可以识别多个数据元素(或记录)的关键字,我们成为次关键字。次关键字可以理解为不以唯一标识一个数据元素(或记录)的关键字,它对应的数据项就是次关键码
2.查找表分类(按操作方式)
静态查找表 动态查找表
静态查找表:只做查找操作的查找表
(1)查询某个“特定的”数据元素是否在查找表中
(2)检索某个“特定的”数据元素及其各种属性
动态表查找:在查找的过程中同时插入不存在的数据元素或者从查找表中删除已经存在的某个数据元素
(1)查找时插入数据元素
(2)查找时删除数据元素
3.改进
为了提高查找效率,我们需要专门为查找操作设置数据机构,这种面向查找操作的数据结构称为查找结构
从逻辑上来说,查找所基于的数据结构是集合,集合中的记录之间没有本质关系 可是要想获得较高的查找性能,我们就能不改变数据元素之间的关系,在存储时可以将查找表组织成表、树结构
例如:对于静态查找表来说,我们不妨应用线性表结构来组织数据,这样可以使用顺序查找算法,如果再对主关键字排序,则可以应用折半查找计数进行高效的查找。
如果需要动态查找,则会复杂一些,可以考虑二叉排序树的查找计数
另外,可以使用散列计数来解决一下查找问题
二.顺序表查找
1.原理
地毯式的比较,只需要从头比到尾,中间如果找到需要找的Key的时候,就返回这个Key的下标,如果全程都找不到的话,那就返回一个0,表示为没有找到 (下标为0的位置我们是不会进行存放数据的)
2.代码
3.优化
每次循环都要对i <= n进行判断
在比较的时候为了不用每一次都要比较,会设置一个哨兵,也就是在下标为0的地方预先存放好了要寻找的内容,这样的话就节省了比较是否大于等于1这一步
for循环————>while循环
哨兵的位置不一定要在数组开头
三.有序表查找
1.折半查找(二分查找)
①原理
它的前提就是线性表中的记录必须有序的,线性表必须采用顺序存储。
基本思想:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大雨中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止
②代码
2.插值查找
①原理
插值查找是根据要查找的关键字key与查找表汇总的最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式
②代码
3.斐波那契查找
(1)原理
根据斐波那契序列的特点对有序表进行分割的。要求开始表中记录的个数为某个斐波那契数小1,即n=F(k)-1
开始将k值与第F(k-1)位置的记录进行比较(及**mid=low+F(k-1)-1)**比较结果也分为三种
(F[k] = F[k - 1] + F[k - 2])
①相等,mid位置的元素即为所求
②> low=mid+1 k-=2 说明low=mid+1说明待查找的元素在[mid+1,high]范围内 k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个 所以可以递归的应用斐波那契查找
③< high=mid-1 k-=1 说明low=mid+1说明待查找的元素在[low,mid-1]范围内,k-=1 说明范围[low,mid-1]内的元素个数为F(k-1)-1个,所以可以递归的应用斐波那契查找
(2)代码
F[k] - 1是为了配合数组从0开始的
//斐波那契查找 首先需要一个斐波那契数列
int Fibonacci_Search(int *a, int n, int key)
{
int low, high, mid, i, k;
low = 1;
high = n;
k = 0;
while(n > F[k] - 1) //计算n位于斐波那契数列的位置
{
k++;
}
for(i = n; i < F[k] - 1; i++) //将不满的数值补全
{
a[i] = a[n];
}
while(low <= high)
{
mid = low + F[k - 1] - 1;
if(key < a[mid])
{
high = mid - 1;
k -= 1;
}
else if(key > a[mid])
{
low = mid + 1;
k -= 2;
}
else
{
if(mid <= n)
{
return mid;
}
else
{
return n;
}
}
}
return 0;
}
四.线性索引查找
索引就是把一个关键字与它对应的记录相关联的过程,一个索引由若干个索引项构成,每个索引项至少应包含关键字和其对应的记录在存储器中的位置等信息。
索引按照结构可以分为线性索引、树形索引和多级索引。我们这里就只介绍线性索引技术。所谓线性索引就是将索引项集合组织为线性结构,也称为索引表。我们重点介绍三个线性索引:稠密索引、分块索引、和倒排索引。
1.稠密索引
指线性索引中,将数据集中的每个记录对应一个索引项。
对于稠密索引这个索引表来说,索引一定是按照关键码有序排列的 索引项有序也就意味着,我们要查找关键字时,可以用到折半、插值、斐波那契等有序查找算法,大大提高效率。
2.分块索引
稠密索引因为索引项与数据集的记录个数相同,所以空间代价很大。为了减少索引的个数,我们可以对数据集进行分块,使其分块有序,然后再对每一块建立一个索引项,从而减少索引项的个数。
分块有序,是把数据集的记录分成若干块,并且这些块需要满足两个条件:
① 块内无序 即每一块内的记录不要求有序。当然,你如果能够让块内有序对查找来说更理想,不过这就要付出大量时间和空间代价,因此通常我们不要求块内有序
② 块间有序 例如要求第二块所有记录的关键字均要大于第一块中所有记录的关键字,第三块的所有记录的关键字均要大于第二块的所有记录关键字….因为只有块间有序,才有可能在查找时带来效率。
对于分块有序的数据集,将每块对应一个索引项,这种索引方法叫做分场索引。
我们定义的分块索引项结构分三个数据项
n 最大关键码:它存储每一块中的最大关键字,这样的好处就是可以使得在它之后的下一块中最小关键字也能比这一块最大的关键字要大
n 存储了块中的记录个数,以便于循环时使用
n 用于指向块首数据元素的指针,便于开始对这一块中的记录进行遍历。
分块索引是分两步进行:
① 在分块索引表中查找要查关键字所在的块。由于分块索引表是块间有序的,因此很容易利用折半、插值等算法得到结果
②根据块首指针找到相应的块,并在块中顺序查找关键码**(可能是无序的)**
3.倒排索引
倒排索引源于实际应用中需要根据**属性(或字段、次关键码)**的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性来确定记录的位置,因而称为倒排索引。
倒序索引的倒序,指的是这个索引是从关键词中查找对应的源的,而不是从源中检索对应的关键词。
索引项的通用结构
①次关键码
②记录号表:存储具有相同次字关键字的所有记录的记录号(可以指向记录的指针或者是该记录的主关键字)
倒排索引的优点显然是查找记录非常快,基本等于生成索引表后,查找时都不用去读取记录,即可以得到结果。但是它的缺点是这个记录号不定长,可多可少。