折半查找
折半查找(Binary Search)技术,又称二分查找,它的前提是线性表中的记录必须是关键码有序(通常从大到小),线性表必须采用顺序存储。折半查找的基本思想是:
- (不用看了,都是些废话)在有序表中,取中间值作为比较对象,若给定值与中间记录的关键字相等,则查找成功,若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复,直到查找成功或查找失败。
折半查找案例:
/* 折半查找 */
int Binary_Search(int *a,int n,int key)
{
int low,high,mid;
low=1; /* 定义最低下标为记录首位 */
high=n; /* 定义最高下标为记录末位 */
while(low<=high)
{
mid=(low+high)/2; /* 折半 */
if (key<a[mid]) /* 若查找值比中值小 */
high=mid-1; /* 最高下标调整到中位下标小一位 */
else if (key>a[mid])/* 若查找值比中值大 */
low=mid+1; /* 最低下标调整到中位下标大一位 */
else
{
return mid; /* 若相等则说明mid即为查找到的位置 */
}
}
return 0;
}
时间复杂度O(logn)。
插值查找
插值查找(Interpolation Search)是根据要查找的关键字 key 与查找表中最大最小记录的关键字比较后的查找方法,其核心在于插值的计算公式 (key - a[low]) / (a[high] - a[low])。 它的时间复杂度也是 O(logn),但对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好得多。反之,如果分布极端不均匀的数据,用插值法查找未必是合适的选择。
* 插值查找 */
int Interpolation_Search(int *a,int n,int key)
{
int low,high,mid;
low=1; /* 定义最低下标为记录首位 */
high=n; /* 定义最高下标为记录末位 */
while(low<=high)
{
mid=low+ (high-low)*(key-a[low])/(a[high]-a[low]); /* 插值 */
if (key<a[mid]) /* 若查找值比插值小 */
high=mid-1; /* 最高下标调整到插值下标小一位 */
else if (key>a[mid])/* 若查找值比插值大 */
low=mid+1; /* 最低下标调整到插值下标大一位 */
else
return mid; /* 若相等则说明mid即为查找到的位置 */
}
return 0;
}
斐波那契查找
/* 斐波那契查找 */
int Fibonacci_Search(int *a,int n,int key)
{
int low,high,mid,i,k=0;
low=1; /* 定义最低下标为记录首位 */
high=n; /* 定义最高下标为记录末位 */
while(n>F[k]-1)
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; /* 若相等则说明mid即为查找到的位置 */
else
return n;
}
}
return 0;
}
线性索引查找——基于数据无序要求所建的索引
前面的查找方法都是基于有序的基础之上的。但事实上,许多数据集可能增长非常快,要保证记录全部是按照当中的某个关键字有序,它的时间代价是非常高昂的,所以这种数据通常需要按先后顺序存储。(可能是时间先后)
对于这样的查找表,我们如何快速找到需要的数据? ——索引。
数据结构的最终目的是提高数据的处理速度,索引是为了加快查找速度而设计的一种数据结构。索引就是把一个关键字与它对应的记录相关联的过程,一个索引由若干个索引项构成。每个索引项至少应包含关键字和其对应的记录在存储器中的位置等信息。
索引技术是组织大型数据库以及磁盘文件的一种重要技术。
索引按照结构可以分为线性索引、树形索引和多级索引。我们这里就只介绍线性索引技术。线性索引就是将索引项集合组织为线性结构,也称为索引表。
重点介绍:稠密索引、分块索引和倒排索引。
稠密索引
稠密索引是指在线性索引中,将数据集中的每个记录对应一个索引项,
对于稠密索引,索引项一定是按照关键码有序的排列。但是如果数据集非常大,比如上亿,那也就意味着索引也得同样的数据集长度规模,对于内存有限的计算机来说,可能局需要反复去访问磁盘,查找性能反而大大下降了。
分块索引——较少索引项的个数
分块有序,是把数据集的记录分成了若干块,并且这些块需要满足两个条件:
- 块内无序 即每一个块内的记录不要求有序 ,有序当然最好,但那也会付出大量时间和空间的代价,因此可以不要求块内有序。
- 块间有序 例如第二块所有记录的关键字均要大于第一块中所有记录的关键字,第三块的所有记录的关键字均要大于第二块的所有记录关键字……块间有序可以在查找时带来效率。
对于分块有序的数据集,将每块对应于一个索引项,这种索引方法叫做分块索引。我们定义的分块索引的索引结构分为三个数据项:
- 最大关键码, 它存储每一块中的最大关键字,这样的好处就是更方便满足块间有序这一条件。
- 存储了块中的记录个数,以便于循环时使用;
- 用于指向块首数据元素的指针,便于开始对这一块中记录进行遍历。
查找步骤,分两步进行:
- 在分块索引表中查找要查关键字所在的快。由于分块索引表是块间有序的,所以很容易利用折半、插值等算法得到结果。
- 根据块首指针找到的快,并在块中顺序查找关键码。因为块中可以是无序的,所以只能顺序查找。
时间复杂度分析:
- 设 n 个记录的数据集被平均分成 m 块,每个块中有 t 条记录,显然 n = m * t 。
- 再假设 Lb 为查找索引表的平均查找长度,因最好与最差的等概率原则,所以 Lb 的平均长度为 (m + 1) / 2。 Lw 为块中查找记录的平均查找长度,同理它的 平均查找长度为 ( t +1) / 2。
- 这样分块索引查找的平均长度为:
从中可以看出平均长度不仅仅取决于数据集的总记录数 n ,还和每个块的记录块间个数 t 相关,最佳的情况就是分的块数 m 与块中的记录数 t 相同。
分块索引的效率比顺序查找高,但是比折半查找的 O(logn)相比还有不小的差距。 因此在按确定所在块的过程中,由于块间有序,所以可以应用折半、插值等手段提高效率。
倒排索引
倒排索引源于实际应用中需要根据属性(或字段、次关键码)的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而成为倒排索引。
倒排索引的优点是查找记录非常快。基本等于生成索引表后,查找时都不用读取记录,就可以得到结果。但它的缺点就是记录号不定长。(不定长,则存储结构要求就高,维护比较困难)