目录
查找的基本概念
(1)查找表:是由同一类型的数据元素(或记录)构成的集合。由于”集合“中的数据元素之间存在着完全松散的关系,因此查找表是一种非常灵便的数据结构。
(2)关键字:是数据元素(或记录)中某个数据项的值,用它可以标识一个数据元素(或记录)。若此关键字可以唯一地标识一个记录,则称此关键字为主关键字(对不同的记录,其主关键字均不同)。反之,称用以识别若干记录的关键字为次关键字。当数据元素只有一个数据项时,其关键字即该数据元素的值。
(3)查找:是指根据给定的某个值,在查找表中确定一个其关键字等于给定值的记录或数据元素,。若表中存在这样一个记录,则称查找成功,此时查找的结果可给出整个记录的信息,或指示该记录在查找表中的位置;若表中不存在关键字等于定值的记录,则称查找不成功,此时返回一个”空“记录或”空“指针。
(4)动态查找表和静态查找表:若在查找的同时对表执行修改操作(如插入删除),则称相应的表为动态查找表,否则称之为静态查找表。
(5)平均查找长度:为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值,称为查找算法在查找成功时的平均查找长度.
其中,n表示记录的个数 ,pi表示查找第i个记录的概率 ( 通常认为pi =1/n ), ci表示找到第i个记录所需的比较次数。
查找的运算
线性表的查找
顺序查找
适用范围:顺序表或线性链表表示的静态查找表,表内元素之间无序。
数据元素类型的定义
typedef struct
{
KeyType key; //关键字域
InfoType otherinfo; //其他域
}ElemType;
顺序表的定义
typedef struct //顺序表结构类型的定义
{
ElemType *R; //顺序表的基地址
int length; //顺序表的表长
}SSTable;
SSTable ST; //定义顺序表ST
查找的算法
int Search_Seq(SSTable ST,KeyType key)
//在顺序表ST中顺序查找其关键字等于key的数据元素。若找到,则函数值为该元素在表中的位置,否则为0
{
for(i=ST.length;i>=1;--1)
if(ST.R[i].key==key) //从后往前查找
return i;
return 0;
}
但是,该算法在查找过程中每一步都要检测整个表是否查找完毕,每一步都要有循环变量是否满足条件i>=1的检测。改进这个程序,可以免去这个检测的过程。
设置监视哨的顺序查找
把待查关键字key存入表头(“哨兵”),从后向前逐个比较,可免去查找过程中每一步都要检测是否查找完毕,加快速度。
int Search_Seq( SSTable ST , KeyType key )
{
//若成功返回其位置信息,否则返回0
ST.R[0].key =key;
for( i=ST.length; ST.R[ i ].key!=key; - - i );
//不用for(i=n; i>0; - -i) 或 for(i=1; i<=n; i++)
return i;
}
顺序查找的优缺点
优点:算法简单,对表结构无任何要求(顺序和链式)
缺点:n很大时查找效率较低
改进措施:非等概率查找时,可按照查找概率进行排序。
折半查找
折半查找又称为二分查找,它是一种效率高的一种查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中的元素按关键字有序排列。
其查找步骤为L:
1、设表长为n,low、high和mid分别指向待查元素所在区间的上界、下界和中点,key为给定值
2、初始时,令low=1,high=n,mid=(low+high)/2
3、让key与mid指向的记录比较
1>若key==R[mid].key,查找成功
2>若key<R[mid].key,则high=mid-1
3>若key>R[mid].key,则low=mid+1
4、重复上述操作,直至low>high时,查找失败
int Search_Bin(SSTable ST,KeyType key){
//若找到,则函数值为该元素在表中的位置,否则为0
low=1;high=ST.length; while(low<=high){
mid=(low+high)/2;
if(key==ST.R[mid].key) return mid;
else if(key<ST.R[mid].key) high=mid-1;//前一子表查找
else low=mid+1; //后一子表查找
} return 0; //表中不存在待查元素
}
折半查找的优缺点
折半查找优点:效率比顺序表查找高。
折半查找缺点:只适用于有序表,且限于顺序存储结构(对线性链表无效)。
查找过程:每次将待查记录所在区间缩小一半,比顺序查找效率高,时间复杂度O(log2 n)
哈希表的查找
基本思想:记录的存储位置与关键字之间存在对应关系,Loc(i)=H(keyi)
优点:查找速度极快O(1),查找效率与元素个数n无关。
散列表专业术语:
1.哈希方法(杂凑法) :选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放;
查找时,由同一个函数对给定值k计算地址,将k与地址单元中元素关键码进行比,确定查找是否成功。
2.哈希函数(杂凑函数):哈希方法中使用的转换函数。
3.冲 突:不同的关键码映射到同一个哈希地址。
4.同义词:具有相同函数值的两个关键字。
散列函数的构造方法
使用散列表要解决好两个问题:
1)构造好的散列函数
(a)所选函数尽可能简单,以便提高转换速度;
(b)所选函数对关键码计算出的地址,应在散列地址集中致均匀分布,以减少空间浪费。
2)制定一个好的解决冲突的方案
查找时,如果从散列函数计算出的地址中查不到关键码,则应当依据解决冲突的规则,有规律地查询其它相关单元。
除留取余法(最常用的方法)
Hash(key)=key mod p (p是一个整数)
关键:如何选取合适的p?
技巧:设表长为m,取p≤m且为质数
处理冲突的方法
开放地址法
基本思想:有冲突时就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总能找到,并将数据元素存入。
其常用方法:
(1)线性探测法:di为1,2,3,4……m-1的线性序列;
(2)二次探测法:di为1^2,2^2,3^2……q^2 的二次序列;
(3)伪随机探测法:为伪随机数序列。
线性探测法
Hi=(Hash(key)+di) mod m( 1≤ i < m )
其中:m为哈希表长度
di 为增量序列 1,2,…m-1,且di=i(如果冲突就找下一空地址存入)
线性探测法优缺点
优点:只要哈希表未被填满,保证能找到一个空地址单元存放有冲突的元素。
缺点:可能使第i个哈希地址的同义词存入第i+1个地址,这样本应存入第i+1个哈希地址的元素变成了第i+2个哈希地址的同义词,……,产生“聚集”现象,降低查找效率。
链地址法
基本思想:相同哈希地址的记录链成一单链表,m个哈希地址就设m个单链表,然后用用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。
链地址法建立哈希表的方法步骤:
1、取数据元素的关键字key,计算其哈希函数值(地址)。若该地址对应的链表为空,则将该元素插入此链表;否则执行step2解决冲突。
2、根据选择的冲突处理方法,计算关键字key的下一个存储地址。若该地址对应的链表为不为空,则利用链表的前插法或后插法将该元素插入此链表。
链地址法的优点:
非同义词不会冲突,无“聚集”现象
链表上结点空间动态申请,更适合于表长不确定的情况
哈希表的查找
算法步骤:
1、给定待查找的关键字key,根据创建表时设定的散列函数计算H6= H(key)。
2、若单元H。为空,则所查元素不存在。
3、若单元H中元素的关键字为key,则查找成功。
4、否则重复下述解决冲突的过程:
按处理冲突的方法,计算下一个散列地址H;
若单元H为空,则所查元素不存在;
若单元H中元素的关键字为key,则查找成功。
#define NULLKEY 0
//单元为空的标记
int SearchHash (HashTable HT, KeyType key)
{//在散列表HT中查找关键字为key的元素,若查找成功,返回散列表的单元标号,否则返回-1
HO=H(key) ; //根据散列函数H( key )计算散列地址
if (HT[H0] . key==NULLKEY) return -1; //若单元H0为空,则所查元素不存在
else if (HT[H0] .key==key) return H0; //若单元HO中元素的关键字为key,则查找成功
else
{
for(i=l;i<m;++i)
{
Hi= (H0+i) &m; //按照线性探测法计算下一个散列地址Hi
if (HT[Hi]. key--NULLKEY)return -1;//若单元H1为空,则所查元素不存在
else if (HT[Hi] .key==-key) return Hi;//若单元出中元素的关键字为key,则查找成功
} //for
return -1;
}
}//else
使用平均查找长度ASL来衡量查找算法,ASL取决于
1、哈希函数 ;2、处理冲突的方法 ;3、哈希表的装填因子
注:装填因子越大,表中记录数越多,说明表装得越满,发生冲突的可能性就越大,查找时比较次数就越多。