查找
知识框架
在本章中,我们需要熟练掌握以上查找方式,并且由于不同查找方式效率不同,面对不同的问题也要灵活选择
顺序结构
顺序结构的查找适用于顺序表和链表。通常可以分为对无序表的顺序查找以及对关键字有序的线性表的顺序查找。
比如顺序查找是从一端遍历到另一端,是最简单的顺序查找方式
而折半查找则是对有序表每次进行“一半一半”方式的查找,
分块查找则是按照索引进行的顺序查找
一般的顺序结构查找方式为:
typedef struct SSTable(
ElemType *elem;//存储空间的基址,一般我们将0号空间留出用于存储“哨兵”
int TableLen;
};
int search_seq(SSTable ST,ElemType key){//key代表查找的关键字
ST.elem[0]=key;//0号作为哨兵,用于存储我们查找的关键字
for(int i=ST.TableLen;ST.elem[i]!=key;i--){这里面可以带上找到时的操作}
return i;//返回关键字为key时的i,此时体现哨兵作用
//如果没有关键字为key的i,则只有哨兵关键字为key,返回0,代表没找到
//有哨兵的存在就不必担心返回的索引值越界
}
当然可以不用哨兵,引入哨兵是为了减少不必要的判断语句,提高程序效率
顺序查找
顺序查找无非就是从一端搜索到另一端,这要求我们的结构是顺序的结构
void Seq_find(SSTable S,ElemType key){
ST.elem[0]=key;
for(int i=ST.TableLen;ST.elem[i]!=key;i--);//尾部到头部
return i;
}
void Seq_find(SSTable S,ElemType key){
ST.elem[Max_size-1]=key;
for(int i=0;ST.elem[i]!=key;i++);//头部到尾部
return i;
}
我们会使用折半查找
折半查找
折半查找的前提必须在有序表内,因为折半查找在每趟查找过程中,都会将原来的空间从中间元素分为大的一半和小的一半,每次从两半里面选择一块接着进行折半查找,那么每趟搜索后的查找空间都会缩小为原来的一半。
比如对于有序表0 1 2 3 4 5 6,我们若要找到5这个元素,那么第一趟我们会定位到中间,也就是(0+6)/2=3,3是中间元素,以3为中心把查找空间分为了0 1 2和4 5 6,一小一大,我们要查找的5>3,所以第二趟就在大的一块进行查找
重复上述步骤,定位到4 5 6的中间元素(4+6)/2=5,此时找到5返回其位置
int Binary_Search(SeqList L,ElemType key){
int low=0,high=L.length-1;
while(low<=high){
mid=(low+high)/2;
if(L.elem[mid]==key){
return mid;}
else if(L.elem[mid]<key){
low=mid+1;}
else /*if(L.elem[mid]>key){其实没必要判断,写出来只是解释一下*/
high=mid-1;}
}//while
return -1;
}
分块查找
最后是分块查找,分块查找既可以在有序表使用,又可以在无序表使用
分块查找会将原先的一整串查找空间分成若干块,其中其索引表结构包含了两个值域,
第一个值域记录这个分块的最大值,第二个值域记录分块的起始序号
typedef struct IndexTable{
int maxnum;
int Index;//注意这个索引并不是原表的元素序号
//而是新建索引表的
}
(借用别人博客的一张图算法 | 分块查找)
在不改变原数据表的顺序上,我们对其进行分块,使得产生几个最值有序的块
那么我们得到的若干分块(尽可能平均分块),在块间是有序的,可以看作一个有序表
可是对比块内的数据,每个块内是数据又是无序的,因为是属于原来的顺序表的
使用分块查找,我们就可以利用折半查找, 在块间有序的索引表对索引进行查找
而如果索引表内找到了key值,则对其块进行顺序查找
如果找不到key值,则可以对折半查找结束后两边的索引表块都进行顺序查找
虽然查了很多博客都没提到,不过若分块后最值顺序不是有序的应该也是没问题的,我们可以人为排序,虽然打乱了原顺序表的顺序,但是我们是按照索引访问的,所以逻辑上顺序表发生了变动,但物理结构上依旧是不变的,按照索引遍历即可。
//平均分块查找的复杂度是最低的
int blocknum=Max_size/blocksize;
IndexTable IT[blocknum];
//构建索引顺序表
void block_build(Sqtable ST,int key,int blocksize,int blocknum){
for(int i=0;i<blocnum;i++){
IT[i].Index=0+blocksize*i;
IT[i].maxnum=-1;
IT[i].size=blocksize;
}
IT[blocknum-1].size=Max_size-blocksize*blocknum;//最后一块未必长度为blocksize
for(int i=0;i<blocnum;i++){
for(int j=IT[i].Index;j<IT[i].size;j++){
if(ST.elem[j]>IT[i].maxnum){
IT[i].maxnum=ST.elem[j];
}//if
}//for
}//for
Sort(IT);//对IT数组按照其maxnum进行排序
}
int block_Search(Sqtable ST,IndexTable IT,ElemType key,int blocknum){
int low=0,high=blocknum-1;
bool find=False;
while(low<=high){
mid=(low+high)/2;
if(IT[mid].maxnum==key){
find=True;}
else if(IT[mid].maxnum<key){
low=mid+1;}
else
high=mid-1;}
}//while
if(find==True){
for(int i=IT[mid].Index;i<IT[mid].length;i++){
if(ST.elem[i]==key){
return i;}//if
}//for
}//if
else{//没找到的情况
if(key<IT[mid] && mid!=0{//key依旧小于mid且mid不为第一个元素
for(int i=IT[mid-1].Index;i<IT[mid-1].length;i++){
if(ST.elem[i]==key){
return i;}//if
}
}//for
elseif(key>IT[mid] && mid!=blocknum-1{//key依旧小于mid且mid不为最后一个元素
for(int i=IT[mid+1].Index;i<IT[mid+1].length;i++){
if(ST.elem[i]==key){
return i;}//if
}
}//for
else return -1;
}//else
}
void main(){
Sqtable ST;
build(ST);
int key,blocksize,blocknum;cin>>key>>blocksize>>blocknum;
block_build(ST,key,blocksize,blocknum);
block_Search(ST,IT,key,blocknum);
return 0;
}
下半部分树形结构