目录
查找的基本概念
查找表:有同一类型的数据元素构成的集合(或记录)。由于“集合”中的数据元素之间存在着松散的关系,因此查找表是一种应用灵便的结构。没有严格的前驱后继关系。
什么是查找:根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。
关键字:用于表示一个数据元素(或记录)的某个数据项的值。
主关键字:可唯一的标识一个记录的关键字。
次关键字:用以标识若干记录的关键字。
若查找表中存在这样一个记录,则称“查找成功”,查找结果给出整个记录的信息,或指示该记录在查找表中的位置。否则称“查找不成功”,查找结果给出空记录或空指针。
静态查找表:仅作查询操作的查找表。
动态查找表:作插入和删除操作的查找表。
如何评价查找算法:
平均查找长度:ASL(Average Search Length)
顺序查找
应用范围:顺序表或线性链表表示的静态查找表;表内元素无序。
数据元素类型定义:
typedef char KeyType;
typedef struct{
KeyType key;//关键字域
//其他域
}ElemType;
typedef struct{
ElemType *R;//表基址
int length;//表长
}sstable;
//sstable st;
算法实现
(只用查找一次):
int Search_Seq(sstable st,KeyType key){
st.R[0].key = key;
int i;
for(i = st.length;st.R[i].key!=key;i--);
return i;
}
效率分析
时间复杂度:O(n)
空间复杂度:O(1)
如何提高查找效率?
在已知查找概率的情况下,在查找表进行存储时,就按照概率的高低来存储。
若是从前往后找,就将查找概率高的放在前面。
不知道查找概率时:动态调整记录顺序
1.在每个记录中设置一个访问频度域,始终保持记录按有序的次序排列;
2.每次查找后将刚查找到的记录直接移至表头;
顺序查找法的特点:
优点:算法简单,逻辑次序无要求,且不同存储结构均适用。
缺点:ASL太长,时间效率太低。
二分查找(折半查找)
用在有序表示的顺序查找表。
代码实现
int Search_Bin(sstable st,KeyType key){
int low = 1;
int high = st.length;
int mid;
while(low<=high){
mid = (low+high)/2;
if(st.R[mid].key == key)
return mid;
else
if(key<st.R[mid].key)
high = mid -1;
else
low = mid + 1;
}
return 0;
}//用循环的二分查找
int Search_Bin2(sstable st,KeyType key,int low,int high){
if(low>high)
return 0;
int mid;
mid = (low+high)/2;
if(key==st.R[mid].key)
return mid;
else
if(key<st.R[mid].key){
high = mid - 1;
Search_Bin2(st,key,low,high);
}
else{
low = mid+1;
Search_Bin2(st,key,low,high);
}
}//用递归的二分查找
效率分析
紫色矩形中:-1表示比第1个数还小,11-表示比第11个数还大。其他表示要查找的数的大小在那个区间。
第i层表示需要比较的次数。
同时可以看出比较途中的路径,以查找第4个元素为例:先与6比,再与3比,最后和4比。
平均查找长度ASL(成功时):
时间复杂度:O(logn)
优点:效率比顺序查找高。
缺点:只适用于有序表,且限于顺序存储结构(对线性链表无效)
哈希查找(散列查找)
哈希表(散列表)的基本概念
基本思想:记录的存储位置与关键字之间存在对应关系。
对应关系——hash函数
Loc(i) = H(keyi) //H为哈希函数。表示i与其存储位置的关系。例如:H(key)=k;表示k的地址就在k号地址。
优点:查找效率高;
缺点:空间效率低。
散列方法(杂凑法):
选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放;
查找时,由同一个函数对给定值k计算地址,将k与地址单元中元素关键码进行比较,确定查找是否成功。
散列函数(杂凑函数):散列方法中使用的转换函数。
冲突:不同的关键码映射到同一个散列地址
key1!=key2,但是H(key1) = H(key2)
同义词:具有相同函数值的关键字互为同义词。
散列函数的构造方法
要解决的问题
1.构造好的散列函数
a.所选函数尽可能简单,以便提高转换效率
b.所选函数对关键码计算出的地址,应在散列地址集中,均匀分布,以便减少空间浪费。
即要考虑:执行速度,关键字地长度,散列表的大小,关键字的分布情况,查找频率。
2.制定一个好的解决冲突的方案
查找时,如果从散列函数计算出的地址中查找不到关键码,则应当依据解决冲突的规则,有规律地查询其他单元。
地址空间尽量小,元素尽量均匀存放。
直接定址法
Hash(key) = a*key +b (a,b为常数)
优点:以关键字key的某个线性函数值为散列地址,不会产生冲突。
缺点:要占用连续地址空间,空间效率低。
除留余数法
Hash(key) = key mod p
关键:如何选取合适的p?
技巧:设表长为m,取p<=m,且p为质数。
处理冲突的方法
开放定址法(线性探测法和二次探测法)
基本思想:有冲突时就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将数据元素存入。
即:先用hash函数存储,若地址已被占,一个函数Hi来找地址存放,后续一直使用Hi,直到成功。
线性探测法:
二次探测法:
伪随机探测法:
链地址法(拉链表)
基本思想:相同散列地址的记录链成一个单链表。
m个散列地址就设m个单链表,然后用一个数组将m个指针的表头指针存储起来,形成一个动态的结构。
步骤:
链地址法的优点:
非同义词不会冲突,无聚集现象。
链表上的空间动态申请,更适合于表长不确定的情况。
哈希表的查找
在一个链的第几个就要比较几次。
效率分析
几点结论:
1.散列表技术具有很好的平均性能,优于一些传统的技术
2.链地址法优于开地址法
3.除留余数法作散列函数优于其它类型函数