查找在生活中无处不在,电话簿找朋友电话,好友列表找人,聊天记录找历史记录等都用到查找。
比如拿到一个杂乱无章的文章,我们会挨个比对,依次去找某个字,这叫顺序查找;
拿起一本700多页的书,我们想翻到第340页,通常会目测,然后把书从中间翻开,如果翻到380页,就再往前面拿起一部分对半,知道找到;如果翻到320页,则往后面拿起一部分对半分,直到找到。这叫折半查找;
当你拿起一份长长的职工信息表,你想找名字叫“李四”的信息,你会想到按职工号来找把,这体现了索引结构实现查找;
当你想从电话簿中找出姓郭的人,你通常不会从第一个找起,也不会从最中间开始,也不会从最后边开始,你会习惯性的从电话簿前面些的地方开始找,这体现了插值查找;
当你面前有100个人(假设没重名的),你想快速找出名字为“小明”的人,你会怎么做呢?折半,顺序,插值查找?这些显然太low了,你会直接喊某某出来就可以了。这体现了散列表查找的思想。
————————————————————————————————————————————————————————
一、基本概念
(1)查找
人话:按某个标准去查找想要的东西
术语:在数据集合中找出满足某种条件的数据元素
(查找环境分为:静态查找结构和动态查找结构)
(2)查找表
用于查找的数据集合称为查找表,它由同一类数据的数据元素组成。
对应的查找表分为:
静态查找表:不涉及动态修改查找表 ,比如:顺序查找,折半查找,散列查找
动态查找表:需要动态插入或删除查找招表,比如:二叉排序树的查找,散列查找等
(3)平均查找长度
在查找过程中,一次查找的长度是指需要比较的关键字次数,而平均查找长度则是所有查找过程中进行关键字比较次数的平均值,
n为查找表的长度;Pi是查找第i个数据元素的概率,通常认为概率相等为1/n;Ci是查找到第i个数据元素所需要的表较次数。
平均查找长度是衡量查找算法效率的最主要的指标。
二、顺序查找
定义:从线性表的一端开始,逐个检查关键字是否满足给定条件,若满足则查找成功,返回该元素在线性表中的位置。
若已经查找到表的另一端(一般为末端),仍没有查找到符合条件的元素,则返回查找失败的信息。
个人理解的顺序查找,下面进行的是数组上的查找
#include<stdio.h>
#include<stdlib.h>
void Search(int arr[],int n,int key)
{
int i=0;
while(i<n&&arr[i]!=key)
{
i++;
}
if(i>=n)printf("no\n");
else printf("%d\n",i);
}
int main(){
int k1=5,k2=100,i=0;
int arr[]={3,4,5,6,1,2,32,2};
int n=sizeof(arr)/sizeof(int);
Search(arr,n,k1);
Search(arr,n,k2);
return 0;
}
(1)在普通顺序表上的顺序查找
//顺序表上的顺序查找算法
int SeqSearch(SeqList & L,DataType x){
int i=0;
while(i<L.n&&L.data[i].key!=x.key) i++;
return i;
}//
//使用了“监视哨”的顺序查找
int Search(SeqList & L,DataType x){
L.data[L.n]=x;//将x设为监视哨
int i=0;
While(L.data[i].key!=x.key);//从前往后顺序查找
return i;
};
监视哨:
若找到则函数返回元素在该表中的位置,否则返回n。与无监视哨的算法相比,每次少了控制循环结束的条件判断语句i<L.n。
typedef struct{
Elemtype *elem; //元素空间基址,建表时按实际长度分配,0号单元留空
int TableLen; //表的长度
}SStable;
int Search_Seq(SSTable ST,Elemtype key){
//在顺序表中关键字为key的元素,若找到则返回该元素在表中的位置
ST.elem[0]=key; //哨兵
for(i=ST.TableLen;ST.elem[i]!=key;--i); //从后往前找
return i;//若表中不存在关键字为key的元素,将i为0时退出for循环
}
上面这个从后往前找,把第一个设为哨兵,思想就是将key值赋给表中第一个元素,当当前值不等于于key,就一直往前找,当当前值等于哨兵(也就是key),返回i
(2)顺序查找的递归
int SeqSearch(SeqList &L,DataType x,int lic)
{ if(loc>=L.n) return -1; //查找失败
else if(L.data[loc].key==x.key) //查找成功
return loc;
else return SeqSearch(L,x,loc+1); //递归查找后续部分
}
(3)在有序顺序表上的顺序查找
int SqeSearch(SeqList & L,DataType x){
for(int i=0;i<L.n;i++)
if(L.data[i].key==x.key) return i;
else if(L.data[i].key>x.key) break;
return -1;
}
查找成功的平均长度在等概率下为 n+1/2
查找不成功的平均长度在等概率下为 n/2+n/(n+1)
三、折半查找
基本思想为:
(1)若data[mid]=x,查找成功则报告成功并返回下标
(2)若x>data[mid],把查找区间缩小到表的前半部分,再继续进行折半查找
(3)若x<data[mid],把查找区间缩小到表的后半部分,再继续进行折半查找
非递归折半查找
int BinarySearch(SeqList &L,DataType x){
int high=L.n-1,low=0,mid;
while(low<high){
mid=(low+high)/2;
if(x.key>L.data[mid].key) low=mid+1;
else if(x.key<L.data[mid].key) high=mid-1;
else return mid+1;
}
return -1;
}
//递归折半查找
int BinarySearch(SeqList & L,DtaType x,int low,int high){
int mid=-1;
if(low<=high){
mid=(low+high)/2; //求中点
if(x.key>L.data[mid].key) //大于中点值,右缩查找区间
mid=BinarySearch(L,x,mid+1,high);
else if(x<L.data[mid].key) //小于中点值,左缩查找区间
mid=BinarySearch(L,x,low,mid-1);
}//等于中点值,继续向下执行
return mid; //返回中点位置或-1
}
对n个关键码有序顺序表,最多比较 次 ,平均查找长度
折半查找仅使用有序顺序表
#include<stdio.h>
#include<stdlib.h>
int binsearch(int arr[],int n,int key){
int low=0;
int high=n-1;
while(low<=high){
int mid=low+((high-low)>>1);
if(arr[mid]==key) {
return mid;
continue;
}
else if(arr[mid]<key) {
low=mid+1;
}else{
high=mid-1;
}
}
return -1;
}
int main(){
int k1=8;
int arr[]={3,4,5,6,1,2,32,2};
int n=sizeof(arr)/sizeof(int);
printf("%d",binsearch(arr,n,k1));
return 0;
}