查找
根据给定的某个值,在查找表中确定一个其关键字(唯一的标识一个记录)等于给定值的数据元素或数据记录。
静态查找:只查找,不修改元素[线性表、顺序查找、二分查找]
动态查找:查找时,插入或者删除元素[二叉排序树]
顺序表查找
顺序查找(针对静态查找表),也叫线性查找O(n),从头开始遍历,直到最后一个记录。
优化:添加哨兵
//有哨兵的顺序查找
int foo(int *a,int n,int key)
{
int i;
a[0]=key;//哨兵
i=n;
while(a[i]!=key)
{
i--;
}
return i;
}
有序表查找
1.二分查找O(logn):
前提元素有序,原因具有n个节点的完全二叉树的深度为[logn]+1(取下)。尽管不是完全二叉树,但是同样的推导,最坏找到最后一层。
不适合动态查找。
2.插值查找O(logn)
根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找方法。
二分基础上修改,low+1/2(high-low)中1/2–>(key-a[low])/(a[high]-a[low])(插值公式)
3.斐波那契查找O(logn)
黄金分割原理。
斐波那契数列
//斐波那契查找
int foo(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=k-1;
}else if(key>a[mid])
{
low=mid+1;
k=k-2;
}else
{
if(mid<=n)
return mid;
else
return n;
}
}
return 0;
}
斐波那契查找算法的核心:
- 当key=a[mid],成功;
- 当key< a[mid],新范围是第low个到第mid-1个,此时范围个数为F[k-1]-1个;
- 当key> a[mid],新范围是第m+1个到第high个,此时范围个数为F[k-2]-1个。
平均性能优于二分查找。
有序表查找的三种方法中,二分进行加法与除法运算,插值查找进行复杂的四则运算,而斐波那契查找只是进行最简单加减法运算,在海量数据的查找过程中,这种细微的差别可能会影响最终的查找效率。
三种查找的本质是分隔点的选择不同。
线性索引查找
将索引项集合组织为线性结构,即索引表。
1.稠密索引:每个记录对应一个索引项,索引项按照关键码有序,适合少量数据。
查找时,可用二分,插值等从索引表中查找关键码。
数据量大,意味着索引也得同样的数据集长度规模,不适用。
2.分块索引:块内无序(二分等),块间有序(顺序查找)。每块对应一个索引,
类似图书馆藏书原理。
n个记录的数据集被平均分为m块,每个块中有t条记录。n=mxt。
Lb为查找索引表的平均查找长度。Lb=(m+1)/2,
Lw为块中查找记录的平均查找长度。Lw=(t+1)/2。
分块索引查找的平均查找长度为:
公式依赖n、t两个变量。
最好情况m=t,此时
比顺序查找O(n)快,比二分O(logn)慢。
3.倒排索引
由属性(字段、关键字)的值确定记录的位置。
这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。
由于不是有记录来确定属性值,而是有属性值来确定记录的位置,因而叫倒排索引。
优点:查找记录快
缺点:记录号不定长。
应用:Google、百度搜索引擎。