查找
主要讨论顺序表、有序表、索引表和哈希表查找的各种实现方法,以及相应查找方法在等概率情况下的平均查找长度。
查找表(Search Table):相同类型的数据元素(对象)组成的集合,每个元素通常由若干数据项构成。
关键字(Key,码):数据元素中某个(或几个)数据项的值,它可以标识一个数据元素。若关键字能唯一标识一个数据元素,则关键字称为主关键字(Primary Key) ;将能标识若干个数据元素的关键字称为次关键字(Secondary Key) 。
查找/检索(Searching):根据给定的K值,在查找表中确定一个关键字等于给定值的记录或数据元素。
◆ 查找表中存在满足条件的记录:查找成功;结果:所查到的记录信息或记录在查找表中的位置。
◆ 查找表中不存在满足条件的记录:查找失败。
根据查找表的操作方式,分为静态查找表和动态查找表。
静态查找表(Static Search): 只对查找表进行如下操作:
(1)查看某个特定的数据元素是否在查找表中,
(2)检索某个特定元素和各种属性。
动态查找表(Dynamic Search):在查找过程中,可进行如下操作:
(1)可以将查找表中不存在的数据元素插入,
(2)从查找表中删除已存在的某个记录。
为了提高查找的效率,除了应用好的查找算法外,也会为查找设立专门的数据结构来存储数据,比如表、树等等。
查找表的存储结构:
查找表是一种非常灵活的数据结构,可以用多种方式来存储。
根据存储结构的不同,查找方法可分为三大类:
① 顺序表和链表的查找:将给定的K值与查找表中记录的关键字进行比较, 找到要查找的记录;
② 散列表的查找:根据给定的K值直接访问查找表, 从而找到要查找的记录;
③ 索引查找表的查找:首先根据索引确定待查找记录所在的块 ,然后再从块中找到要查找的记录。
一般地,认为记录的关键字是一些可以进行比较运算的类型,如整型、字符型、实型等,本章以后各节中讨论所涉及的关键字、数据元素等的类型描述如下:
典型的关键字类型说明是:
typedef float KeyType ; /* 实型 */
typedef int KeyType ; /* 整型 */
typedef char KeyType ; /* 字符型 */
数据元素类型的定义是:
typedef struct RecType
{ KeyType key ; /* 关键字码 */
┇ /* 其他域 */
}RecType ;
静态查找
静态查找表的抽象数据类型定义如下:
ADT Static_SearchTable{
数据对象D:D是具有相同特性的数据元素的集合,
各个数据元素有唯一标识的关键字。
数据关系R:数据元素同属于一个集合。
基本操作P:
Create
Destroy
Search
Traverse
} ADT Static_SearchTable
线性表是查找表最简单的一种组织方式,本节介绍几种主要的关于线性表的查找方法。
顺序查找
适用场合:针对查找表中的元素没有按关键字进行排序(即无序线性表)
1 查找思想
从表的一端(第一个或 最后一个)开始逐个将记录的关键字和给定K值进行比较,若某个记录的关键字和给定K值相等,查找成功;否则,若扫描完整个表,仍然没有找到相应的记录,则查找失败。顺序表的类型定义如下:
#define MAX_SIZE 100
typedef struct SSTable
{ RecType elem[MAX_SIZE] ; /* 顺序表 */
int length ; /* 实际元素个数 */
}SSTable ;
int Seq_Search ( SSTable ST , KeyType key)
{ int i ;
ST. elem[0].key=key ; /* 设置监视哨兵,失败返回0 */
for (i=ST.length; i>=1;i--)
if (ST. elem[i].key==key)
return i;
return 0;
}
折半查找
前提条件:查找表中的所有记录是按关键字有序(升序或降序) 。
查找过程中,先确定待查找记录在表中的范围,然后逐步缩小范围(每次将待查记录所在区间缩小一半),直到找到或找不到记录为止。
1 查找思想
用Low、High和Mid表示待查找区间的下界、上界和中间位置指针,初值为Low=1,High=n。
⑴ 取中间位置Mid:Mid=?(Low+High)/2? ;
⑵ 比较中间位置记录的关键字与给定的K值:
① 相等: 查找成功;
② 大于:待查记录在区间的前半段,修改上界指针: High=Mid-1,转⑴ ;
③ 小于:待查记录在区间的后半段,修改下界指针:Low=Mid+1,转⑴ ;
直到越界(Low>High),查找失败。
2 算法实现
int Bin_Search(SSTable ST , KeyType key)
{ int Low=1,High=ST.length, Mid ;
while (Low<High)
{ Mid=(Low+High)/2 ;
if (ST. elem[Mid].key== key))
return(Mid) ;
else if (ST. elem[Mid].key<key))
Low=Mid+1 ;
else High=Mid-1 ;
}
return(0) ; /* 查找失败 */
}
Fibonacci查找
Fibonacci查找方法是根据Fibonacci数列的特点对查找表进行分割。Fibonacci数列的定义是:
F(0)=0,F(1)=1,F(j)=F(j-1)+F(j-2) 。
若共有20(F(8)-1)个数, j=8,则mid= low+F(7)-1 =13
若key <a[13], 则low=1,high=12
下一步:j=7, mid= low+F(6)-1 =8
若key >a[13], 则low=14,high=20( F(8) -1)
下一步:j=6, mid=low+F(5)-1=18
1 查找思想
设查找表中的记录数比某个Fibonacci数小1,即设n=F(j)-1。用Low、High和Mid表示待查找区间的下界、上界和分割位置,初值为Low=1,High=n。
⑴ 取分割位置Mid:Mid=low+F(j-1)-1;
⑵ 比较分割位置记录的关键字与给定的K值:
① 相等: 查找成功;
② 大于:待查记录在区间的前半段(区间长度为F(j-1)-1),修改上界指针: High=Mid-1,转⑴ ;
③ 小于:待查记录在区间的后半段(区间长度为F(j-2)-1),修改下界指针:Low=Mid+1,转⑴ ;
直到越界(Low>High),查找失败。
算法实现
在算法实现时,为了避免频繁计算Fibonacci数,可用两个变量f1和f2保存当前相邻的两个Fibonacci数,这样在以后的计算中可以依次递推计算出。
int fib(int n)
{ int i, f , f0=0 , f1=1 ;
if (n==0) return(0) ;
if (n==1) return(1) ;
for (i=2 ; i<=n ; i++ )
{ f=f0+f1 ; f0=f1 ; f1=f ; }
return(f) ;
}
int Fib_search(RecType ST[] , KeyType key , int n)
/* 在有序表ST中用Fibonacci方法查找关键字为key的记录 */
{ int Low=1, High, Mid, f1, f2 ;
High=fib(n)-1 ; f1=fib(n-1) ; f2=fib(n-2) ;
while (Low<=High)
{ Mid=Low+f1-1;
if ( ST.[Mid].key==key ) return(Mid) ;
else if ( key<ST.[Mid].key) )
{ High=Mid-1 ; f2=f1-f2 ; f1=f1-f2 ; }
else
{ Low=Mid+1 ;f1=f1-f2 ; f2=f2-f1 ; }
}
return(0) ;
}
Fibonacci查找的时间复杂度也为O(logn)
平均性能,优于折半查找;但在最坏情况下性能比折半查找差。
折半查找需进行加法和除法运算;但Fibonacci查找只需进行加、减运算。
分块查找
分块查找(Blocking Search)又称索引顺序查找,是前面两种查找方法的综合。
1 查找表的组织
① 将查找表分成几块。块间有序,即第i+1块的所有记录关键字均大于(或小于)第i块记录关键字;块内无序。
② 在查找表的基础上附加一个索引表,索引表是按关键字有序的,索引表中记录的构成是
应用
分块索引兼顾了对细分块不需要有序的情况下,大大增加了整体查找速度,普遍应用于数据库表查找技术中。
算法实现
typedef struct IndexType
{ keyType maxkey ; /* 块中最大的关键字 */
int startpos ; /* 块的起始位置 */
}Index;
int Block_search(RecType ST[] , Index ind[] , KeyType key , int n , int b)
/* 在分块索引表中查找关键字为key的记录 */
/*表长为n ,块数为b */
{ int i=0 , j , k ;
while ((i<b)&<(ind[i].maxkey, key) ) i++ ;
if (i>b) { printf("\nNot found"); return(0); }
j=ind[i].startpos ;
while ((j<n)&&LQ(ST[j].key, ind[i].maxkey) )
{ if ( EQ(ST[j].key, key) ) break ;
j++ ;
} /* 在块内查找 */
if (j>n||!EQ(ST[j].key, key) )
{ j=0; printf("\nNot found"); }
return(j);
}