查找的基本概念
- 查找定义:
根据给定的某个值,在查找表中确定一 个其关键字等于给定值的数据元素或(记录)。
- 查找表:
是由同一类型的数据元素(或记录)构成的集合。由于"集合”中的数据元素之间存在着松散的关系,因此查找表是一种应用灵便的结构。
- 关键字:
用来标识一个数据元素(或记录)的某个数据项的值。
主关键字:可唯一地标识一个记录的关键字是主关键字;
次关键字:反之,用以识别若干记录的关键字是次关键字。
- 查找成功与否:
查找成功:若查找表中存在这样一个记录, 查找结果给出整个记录的信息,或指示该记录在查找表中的位置;
查找不成功:查找结果给出"空记录”或"空指针”。
- 查找表可分为两类:
① 静态查找表:仅作"查询"(检索)操作的查找表。
② 动态查找表:作“插入"和"删除”操作的查找表。
有时在查询之后,还需要将"查询”结果为“不在查找表中”的数据元素插入到查找表中;或者从查找表中删除其“查询”结果为"在查我表中”的数据元素,此类表为动态查找表。
- 查找算法的评价指标:
关键字的平均比较次数,也称平均查找长度ASL。
n:记录的个数。
pi:查找第i个记录的概率(通常认为pi =1/n )。
ci:找到第i个记录所需的比较次数。
线性表的查找
顺序查找(线性查找)
顺序查找算法
应用范围:
顺序表或线性链表表示的静态查找表;
表内元素之间无序。
数据元素类型定义:
typedef struct{
KeyType key;//关键字域
......//其他域
}ElemType;
顺序表定义:
typedef struct{//顺序表结构类型定义
ElemType *R;//表基址
int length;//表长
}SSTable;//Sequential Search Table
SSTable ST;//定义顺序表ST
查找算法:
int Search_Seq(SSTable ST,keyType key){//若成功返回其位置信息,否则返回0
for(i=ST.length;i>=1;--i)//这里是从后往前找
if(ST.R[i].key==key)
return i;
return 0;
}
//其他形式:
一、
int Search_Seq(SSTable ST,KeyType key){
for(i=ST.length;ST.R[i].key!=key;--i)
if(i<=0)
break;
if(i>0)
return i;
else
return 0;
}
二、
int Search_Seq(SSTable ST,KeyType key){
for(i=ST.length;ST.R[i].key!=key&&i>0;--i);
if(i>0)
return i;
else
return 0;
}
//每执行一次循环都要进行两次比较。
改进查找算法:
把待查关键字key存入表头( "哨兵”、监视哨”),从后往前逐个比较,可免去查找过程中每一步都要检测是否查找完毕,加快速度。
如果后面没有匹配的,查找到第0个是key,则返回0。
int Search_Seq(SSTable ST,KeyType key){
ST.R[0].key=key;
for(i=ST.length;ST.R[i].key!=key;--i);
return i;
}
当ST.length较大时,此改进能使进行一次查找所需的平均时间几乎减少一半。
顺序查找算法分析
比较次数与key位置有关:
查找第i个元素,需要比较n-i+1次;
查找失败,需比较n+1次。
上图查找成功时的平均查找长度:
假定每个元素的查找概率相等
ASL=(1+2+ … +11)/11=(11+1)/2=6
- 复杂度分析:
时间复杂度:O(n)
查找成功时的平均查找长度,设表中各记录查找概率相等,平均查找长度ASL(n)=(1+2+ … +n)/n=(n+1)/2
空间复杂度:一个哨兵辅助空间O(1)
- 提高查找效率:
1、记录的查找概率不相等时如何提高查找效率?
查找表存储记录原则——按查找概率高低存储:
① 查找概率越高,比较次数越少;
② 查找概率越低,比较次数较多。
2、记录的查找概率无法测定时如何提高查找效率?
方法——按查找概率动态调整记录顺序:
① 在每个记录中设一个访问频度域;
② 始终保持记录按非递增有序的次序排列;
③ 每次查找后均将刚查到的记录直接移至表头。
- 顺序查找的特点:
优点:算法简单,逻辑次序无要求,且不同存储结构均适用。
缺点:平均查找长度ASL太长,时间效率太低。
折半查找(二分或对分查找)
折半查找:每次有序的将待查记录所在区间缩小一半。
查找过程:
mid=(low+high)/2
key<mid则:high=mid-1
key>mid则:low=mid+1
key==mid,找到
high<low,结束
折半查找算法
非递归算法
【算法思想】
设表长为n,low、high和mid分别指向待查元素所在区间的上界、下界和中点,key为给定的要查找的值:
初始时,令low=1,high=n,mid=⌊(low+high)/2⌋(若是整数则向下取整)
让k与mid指向的记录比较:
若key==R[mid].key,查找成功
若key<R[mid].key,则high=mid-1
若key>R[mid].key,则low=mid+1
重复上述操作,直到low>high时,查找失败
int Search_Bin(SSTable ST,KeyType Key){//在顺序查找表ST上查找key
low=1;//置区间初值
high=ST.length;
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_Bin(SSTable ST,keyType key,int low,int high){//在顺序查找表ST上查找key
if(low>high)
return 0;//查找不到时返回0
mid=(low+high)/2;
if(key==ST.elem[mid].key)
return mid;
else if(key<ST.elem[mid].key)
return Search_Bin(ST,key,low,mid-1);//递归,在前半区间进行查找
else
return Search_Bin(ST,key,mid+1,high);//递归,在后半区间进行查找
}
折半查找算法分析
折半查找的性能分析——判定树
圆形:是内结点,代表查找成功的情况。
矩形:是外结点,代表查找不成功的情况
查找成功:
比较次数=路径上的结点数
比较次数=结点的层数
比较次数≤树的深度=⌊log2n⌋+1
查找不成功:
比较次数=路径上的内部结点数
上图成功时的平均查找长度:
假定每个元素的查找概率相等
ASL=1/11*(11 +22 +43+44 )=33/11=3
- 平均查找长度ASL(成功时):
设表长n=2h-1,则h=log2(n+1) (此时,判定树为深度=h的满二叉树),且表中每个记录的查找概率相等:Pi= 1/n。
- 折半查找的特点:
优点:效率比顺序查找高。
缺点:只适用于有序表,且限于顺序存储结构(对线性链表无效)。
分块查找
分块查找
分块查找又称索引顺序表的查找
条件:
1、将表分成几块,且表或者有序,或者分块有序;
若i<j,则第j块中所有记录的关键字均大于第i块中的最大关键字。
2、建立"索引表"(每个结点含有最大关键字域和指向本
块第一个结点的指针,且按关键字有序)
索引表是有序的;
块内元素是无序的。
查找过程:
先确定待查记录所在块(顺序或折半查找),
再在块内查找(顺序查找)。
分块查找性能分析
分块查找优缺点:
优点:插入和删除比较容易,无需进行大量移动。
缺点:要增加一个索引表的存储空间并对初始索引表进行排序运算。
适用情况:如果线性表既要快速查找又经常动态变化,则可采用分块查找。