关键字与主关键字:数据元素中某个数据项的值。如果该关键字可以将所有的数据元素区别开来,也即可以唯一标识一个数据元素,则该关键字称为主关键字,否则称为次关键字。特别地,如果数据元素只有一个数据项,则数据元素的值即是关键字。
查找表:是由同种类型的数据元素构成的集合。查找表中的数据元素是完全松散的,数据元素之间没有直接的联系。
查找:根据关键字在特定的查找表中找到一个与给定关键字相同的数据元素的操作。如果在表中找到相应的数据元素,则称查找是成功的,否则称查找是失败的。
对查找表经常进行的操作有查询某个“特定的”数据元素是否在查找表中、检索某个“特定的”数据元素的各种属性、在查找表中插入一个数据元素、从查找表中删除某个数据元素。若对查找表只进行前两种操作,则称此类查找表为静态查找表,相应的查找方法称为静态查找。若在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已存在的某个数据元素,则称此类查找为动态查找表,相应的查找方法为动态查找。
平均查找长度:是指在查找过程中,需要比较关键字的平均次数,它是衡量查找算法的效率标准。平均查找长度的数学定义式为
ASL=∑ni=1PiCi
。其中
Pi
表示查找表中第i个数据元素的概率,
Ci
表示 找到第i个数据元素时与关键字比较的次数。
1.顺序表的查找
顺序表的查找过程为从表的一端开始,逐个与关键字进行比较,若某个数据元素的关键字与给定的关键字相等,则查找成功,函数返回该数据元素所在的顺序表的位置;否则查找失败,返回0。
- 类型定义文件
#define MaxSize 100
typedef int KeyType;
typedef struct /*元素的定义*/
{
KeyType key;
}DataType;
typedef struct /*顺序表的类型定义*/
{
DataType list[MaxSize];
int length;
}SSTable;
- 顺序查找函数
int SeqSearch(SSTable S,DataType x)
/*在顺序表中查找关键字为x的元素,如果找到返回该元素在表中的位置,否则返回0*/
{
int i=0;
while(i<S.length&&S.list[i].key!=x.key) /*从顺序表的第一个元素开始比较*/
i++;
if(S.list[i].key==x.key)
return i+1;
else
return 0;
}
以上算法也可以通过设置监视哨的方法实现:
int SeqSearch2(SSTable S,DataType x)
{
/*设置监视哨S.list[0],在顺序表中超找关键字为x的元素,如果找到返回该元素在表中的位置,否则返回0*/
int i=S.length;
S.list[0].key=x.key; /*将关键字存放在第0号位置,防止越界*/
while(S.list[i].key!=x.key)/*从顺序表的最后一个元素开始向前比较*/
i--;
return i;
}
其中S.list[0]被称为监视哨,可以防止出现数组越界。
假设表中有n个数据元素,且数据元素在表中出现的概率都相等,即
1n
,则顺序表在查找成功时的平均查找长度为
ASL成功=∑ni=1PiCi=∑ni=11n∗(n−i+1)=n+12
,即查找成功时平均比较次数约为表长的一半。在查找失败时,即要查找的元素没有在表中,则每次比较都需要进行n+1次。
2.有序顺序表的查找
有序顺序表就是顺序表中的元素是以关键字进行有序排列的。对于有序顺序表的查找有两种方法,即顺序查找和折半查找。
顺序查找
有序顺序表的查找算法与顺序表的查找算法类似。在通常情况下,无须比较表中的所有元素。如果要查找的元素在表中,则返回该元素的标号,否则返回0。
int SeqSearch3(SSTable S,DataType x)
{
/*设置监视哨S.list[0],在有序顺序表中查找关键字为x的元素,如果找到返回该元素在表中的位置,否则返回0*/
int i=S.length;
S.list[0].key=x.key;/*将关键字存放在第0号位置,防止越界*/
while(S.list[i].key>x.key)/*从有序顺序表的最后一个元素开始向前比较*/
i--;
return i;
}
设表中有n个数据元素,且数据元素在表中出现的概率都相等,即 1n ,则有序顺序表在查找成功时的平均查找长度为 ASL成功=∑ni=1PiCi=∑ni=11n∗(n−i+1)=n+12 ,即查找成功时平均比较次数约为表长的一半。在查找失败时,即要查找的元素没有在表中,则有序顺序表在查找失败时的平均查找长度为 ASL失败=∑ni=1PiCi=∑ni=11n∗(n−i+1)=n+12 ,即查找失败时平均比较次数也同样约为表长的一半。
折半查找
又称为二分查找,这种查找算法要求待查找的元素序列必须是从小到大排列的有序序列。
折半查找即将待查找的元素与表中的元素进行比较,如果两者相等,在说明查找成功,否则利用中间位置将表分成两个部分;如果待查找元素小于中间位置的元素值,则继续与前一个子表的中间位置元素进行比较,否则与后一个子表的中间位置进行比较;不断重复以上操作,直到找到与待查找元素相等的元素,表明查找成功;如果子表变为空表,表明查找失败。
例如一个有序顺序表为(9,23,26,32,36,47,56,63,79,81),如果要查找的元素为56,利用折半查找算法的查找过程如下:
图中low和high两个指针,分别指向待查找元素的下界和上界,指针mid指向low和high的中间位置,即mid=(low+high)/2。
int BinarySearch(SSTable S,DataType x)
/*在有序顺序表中折半查找关键字为x的元素,如果找到返回该元素在表中的位置,否则返回0*/
{
int low,high,mid;
low=0,high=S.length-1; /*设置待查找元素范围的下界和上界*/
while(low<=high)
{
mid=(low+high)/2;
if(S.list[mid].key==x.key) /*如果找到元素,则返回该元素所在的位置*/
return mid+1;
else if(S.list[mid].key<x.key)/*如果mid所指示的元素小于关键字,则修改low指针*/
low=mid+1;
else if(S.list[mid].key>x.key)/*如果mid所指示的元素大于关键字,则修改high指针*/
high=mid-1;
}
return 0;
}
整个查找过程可以用一个判定树来描述。
如果表中有n个元素,折半查找成功时,至多需要比较的次数为
⌊log2n⌋+1
。
对于具有n个结点的有序表(恰好构成一个深度为h的满二叉树)来说,有
h=⌊log2(n+1)⌋
,二叉树中第i层的结点个数为
2i−1
。假设表中每个元素的查找概率相等,即
Pi=1n
,则有序表在折半查找成功时的平均查找长度为
3.索引顺序表的查找
当顺序表的数据量非常大时,无论使用前述哪种查找算法都需要很长的时间,此时提高查找效率的一个常用方法就是在顺序表中建立索引表。建立索引表的方法就是将顺序表分成几个单元,然后分别为这几个单元建立一个索引,原来的顺序表称为主表,提供索引的表称为索引表。索引表中只存放主表中要查找的数据元素的主关键字和索引信息。
如下图所示,其中索引表包括两部分,即顺序表中每个单元的最大关键字和顺序表中每个单元的第一个元素的下标。
这样的表称为索引顺序表,要使查找效率高,索引表必须有序,但主表中的元素不一定要按关键字有序排列。索引顺序表的查找也称为分块查找。
如果主表中米格单元中的元素个数是不相等,就需要在索引表中增加一项,即用来存储主表中每个单元元素的个数,将这种利用索引表示的顺序表称为不等长索引顺序表。
int SeqIndexSearch(SSTable S,IndexTable T,int m,DataType x)
/*在主表S中查找关键字为x的元素,T为索引表。如果找到返回该元素在表中的位置,否则返回0*/
{
int i,j,bl;
for(i=0;i<m;i++) /*通过索引表确定要查找元素所在的单元*/
if(T[i].maxkey>=x.key)
break;
if(i>=m) /*如果要查找的元素不在索引顺序表中,则返回0*/
return 0;
j=T[i].index; /*要查找的元素在的主表的第j单元*/
if(i<m-1) /*bl为第j单元的长度*/
bl=T[i+1].index-T[i].index;
else
bl=S.length-T[i].index;
while(j<T[i].index+bl)
if(S.list[j].key==x.key)/*如果找到关键字,则返回该关键字在主表中所在的位置*/
return j+1;
else
j++;
return 0;
}
因索引表中的元素的关键字是有序的,故在确定元素所在主表的单元时,既可采用顺序查找发也可采用折半查找法,但对于主表,只能采用顺序法查找。索引顺序表的平均查找长度可以表示为
ASL=Lindex+Lunit
,则
Lindex
是索引表的平均查找长度,
Lunit
是单元中元素的平均查找长度。
假设主表中的元素个数为n,并将主表平均分为b个单元,且每个单元有s个元素,即b=n/s。如果表中的元素查找概率相等,则每个单元中元素的查找概率就是1/s,主表中每个单元的查找概率是1/b。如果用顺序查找法查找索引表中的元素,则索引顺序表查找成功时的平均查找长度为
如果用折半查找法查找索引表中的元素,则有
4.静态查找应用实例
给定一组元素序列,利用顺序表查找、有序顺序表查找和索引顺序表查找值x的元素。
- 类型定义文件
#define MaxSize 100
#define IndexSize 20
typedef int KeyType;
typedef struct /*元素的定义*/
{
KeyType key;
}DataType;
typedef struct /*顺序表的类型定义*/
{
DataType list[MaxSize];
int length;
}SSTable;
typedef struct /*索引表的类型定义*/
{
KeyType maxkey;
int index;
}IndexTable[IndexSize];
- 查找函数文件
int SeqSearch(SSTable S,DataType x)
/*在顺序表中查找关键字为x的元素,如果找到返回该元素在表中的位置,否则返回0*/
{
int i=0;
while(i<S.length&&S.list[i].key!=x.key) /*从顺序表的第一个元素开始比较*/
i++;
if(S.list[i].key==x.key)
return i+1;
else
return 0;
}
int BinarySearch(SSTable S,DataType x)
/*在有序顺序表中折半查找关键字为x的元素,如果找到返回该元素在表中的位置,否则返回0*/
{
int low,high,mid;
low=0,high=S.length-1; /*设置待查找元素范围的下界和上界*/
while(low<=high)
{
mid=(low+high)/2;
if(S.list[mid].key==x.key) /*如果找到元素,则返回该元素所在的位置*/
return mid+1;
else if(S.list[mid].key<x.key)/*如果mid所指示的元素小于关键字,则修改low指针*/
low=mid+1;
else if(S.list[mid].key>x.key)/*如果mid所指示的元素大于关键字,则修改high指针*/
high=mid-1;
}
return 0;
}
int SeqIndexSearch(SSTable S,IndexTable T,int m,DataType x)
/*在主表S中查找关键字为x的元素,T为索引表。如果找到返回该元素在表中的位置,否则返回0*/
{
int i,j,bl;
for(i=0;i<m;i++) /*通过索引表确定要查找元素所在的单元*/
if(T[i].maxkey>=x.key)
break;
if(i>=m) /*如果要查找的元素不在索引顺序表中,则返回0*/
return 0;
j=T[i].index; /*要查找的元素在的主表的第j单元*/
if(i<m-1) /*bl为第j单元的长度*/
bl=T[i+1].index-T[i].index;
else
bl=S.length-T[i].index;
while(j<T[i].index+bl)
if(S.list[j].key==x.key)/*如果找到关键字,则返回该关键字在主表中所在的位置*/
return j+1;
else
j++;
return 0;
}
- 主程序
#include<stdio.h>
#include<stdlib.h>
void main()
{
SSTable S1={{265,55,15,67,9,64,88,50},8};
SSTable S2={{20,28,37,40,46,55,64,76},8};
SSTable S3={{12,8,26,20,19,40,29,43,35,33,56,50,63,55,58,75,64,66,85,79},20};
IndexTable T={{26,0},{43,5},{63,10},{85,15}};
DataType x={64};
int pos;
if((pos=SeqSearch(S1,x))!=0)
printf("顺序表的查找:关键字32在主表中的位置是:%2d\n",pos);
else
printf("查找失败!\n");
if((pos=BinarySearch(S2,x))!=0)
printf("折半查找:关键字32在主表中的位置是:%2d\n",pos);
else
printf("查找失败!\n");
if((pos=SeqIndexSearch(S3,T,4,x))!=0)
printf("索引顺序表的查找:关键字32在主表中的位置是:%2d\n",pos);
else
printf("查找失败!\n");
}
- 测试结果