查找指的是在大量的信息中寻找一个特定的信息元素。
目录
平均查找长度ASL( Average Search Length):对给定k, 查找表L中记录比较次数的期望值(或平均值),用于衡量查找算法的效率。
1.顺序查找
ASL=O(n))
从线性表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值key相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于key的结点,表示查找失败。
int seq_search(int *arr,int key,int len)
{
int i=0;
for(i=0;i<len;i++)
{
if(arr[i]==key)return i;
}
return -1;
}
int main()
{
int arr[5] = { 10, 20, 5, 25, 1 };
int ret = seq_search(arr, 25, 5);
printf("ret pos=%d\n", ret);
return 0;
}
2.折半查找
n-->∞时,ASL=O(log2 (n+1)
也称二分查找,用给定值key先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,直到查找到或查找结束发现表中没有这样的结点。
但查找的前提是数据序列是有序的顺序存储结构,要么升序排列要么降序排列。
void input(int *arr,int *key,int len)
{
int i;
printf("输入数据\n");
for(i=0;i<len;i++)
{
scanf("%d",&arr[i]);
}
printf("输入要查找的数据值\n");
scanf("%d",key);
printf("搜索结果为\n");
}
int bin_search(int *arr,int key,int len)
{
int low=0,high=len-1,mid;
while(low<=high)
{
mid=(low+high)/2;
if(arr[mid]==key)return mid;
else if(arr[mid]<key)low=mid+1;//说明key值可能在右半边,故缩小下界
else high=mid-1;//说明key值可能在左半边,故缩小上界
}
return -1;//未找到返回-1
}
3.哈希查找
ASL=O(c),常数级
要查找key=k的记录时,通过关系f就可得到相应记录的地址而获取记录,从而免去了key的比较过程。这个关系f就是所谓的Hash函数(或称散列函数、杂凑函数),记为H(key)。它实际上是一个地址映象函数,其自变量为记录的key,函数值为记录的存储地址(或称Hash地址)。
另外,不同的key可能得到同一个Hash地址,即当keyl≠key2时,可能有H(key1)=H(key2),此时称key1和key2为同义词。这种现象称为“冲突”或“碰撞”,因为一个数据单位只可存放一条记录。选取Hash函数只能做到使冲突尽可能少,却不能完全避免。这就要求在出现冲突之后,寻求适当的方法来解决冲突记录的存放问题。
本文选择质数除余法(也称保留余数法)作为构造Hash函数的方法,且为较为常用的方法。
设Hash表空间长度为m,选取一个不大于m的最大质数p,则H(key)=key%p
例:记录的key集合k={23,34,14,38,46, 16 ,68 ,15 ,07 ,31 ,26....},假设其哈希表长为m=21,
则取p=19
key : 23 34 14 38 ...
则:H(key)=key%19 : 4 15 14 0 ...
处理冲突的方法有开放地址法,链地址法,本文采用链地址法。发生冲突时,将各冲突记录链在一起,即同义词的记录存于同一链表。
设H(key)取值范围(值域)为[0,m-l],建立头指针向量HP[m],HP[i](0≤i≤m-l)初值为空。凡H(key)=i的记录都链入头指针为HP[i]的链表
另外,导致冲突还有一个因素是表的装填因子α,α=n/m,其中m为表长,n为表中记录个数。一般α在0.7~0.8之间,使表保持一定的空闲余量,以减少冲突和聚积现象。
以上面例子,k={23,34,14,38,46, 16 ,68 ,15 ,07 ,31 ,26},其记录数为11,另装填因子α=0.75,则取哈希表长m=n/α,向上取整为15。故取p=13;则哈希表构建如下:
链地址法解决冲突的优点: 无聚积现象; 删除表中记录容易实现。 而开放地址法的Hash表作删除时, 不能将记录所在单元置空, 只能作删除标记。
以下为质数除余法构造Hash和链地址法解决冲突的C语言实现
主函数:
/*===============================================
* 文件名称:main.c
* 创 建 者: xm
* 创建日期:2022年08月03日
* 描 述:
================================================*/
#include "search.h"
int main(int argc, char *argv[])
{
keytype data[N],key;//数据表和要查的目标数据
//input(data,&key,N);
//测试数据
input_test(data,&key,N);
int i;
L_node *HP[m];//哈希表头,每个元素存储每条链表的头指针
for(i=0;i<m;i++)//初始化表头
{
HP[i]=NULL;
}
for(i=0;i<N;i++)//遍历数据表建立哈希表
{
Set_Hash(HP,data[i]);
}
puts("-------------");
L_node *ret=Hash_search(HP,key);
if(ret==NULL)printf("未找到\n");
else printf("有这个数据结点,其地址为%p\n",ret);
return 0;
}
模块文件:
/*===============================================
* 文件名称:seq_search.c
* 创 建 者:xm
* 创建日期:2022年08月03日
* 描 述:
================================================*/
#include "search.h"
void input(int *arr,int *key,int len)
{
int i;
printf("输入数据\n");
for(i=0;i<len;i++)
{
scanf("%d",&arr[i]);
}
printf("输入要查找的数据值\n");
scanf("%d",key);
printf("搜索结果为\n");
}
//测试数据
void input_test(int *arr,int *key,int len)
{
arr[0]=23;
arr[1]=34;
arr[2]=14;
arr[3]=38;
arr[4]=46;
arr[5]=16;
arr[6]=68;
arr[7]=15;
arr[8]=7;
arr[9]=31;
arr[10]=26;
int i;
for(i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n请输入要查找的数据\n");
scanf("%d",key);
puts("搜索结果");
}
int H(int key)//求哈希地址
{
return key%P;
}
L_node *Hash_search(L_node *HP[m],keytype key)
{
L_node *p=HP[H(key)];//通过Hash地址找到表头对应链表头指针
while((p!=NULL) && (p->key!=key))
{
p=p->next;//产生冲突就取其链表下一结点
}
return p;//q若为NULL说明没找到这个数据,非空则找到且返回其结点地址
}
void Hash_insert(L_node *HP[m],L_node *new_node)//将数据结点插入哈希表
{
int d;
L_node *p=Hash_search(HP,new_node->key);//查找该数据结点在哈希表中位置
if(p!=NULL) printf("记录已存在\n");//说明已有这个数据
else {
d=H(new_node->key);
//头插法
new_node->next=HP[d];
HP[d]=new_node;
}
}
void Set_Hash(L_node *HP[m],keytype key)
{
L_node *new_node=(L_node *)malloc(sizeof(L_node));
if(new_node==NULL)
{
printf("new_node malloc error\n");
return ;
}
new_node->key=key;
new_node->next=NULL;
Hash_insert(HP,new_node);
}
头文件:
/*===============================================
* 文件名称:search.h
* 创 建 者:xm
* 创建日期:2022年08月03日
* 描 述:
================================================*/
#ifndef _SEARCH_
#define _SEARCH_
#include <stdio.h>
#include <stdlib.h>
#define N 11
#define a 0.75 //装填因子
#define m 15 //哈希表长
#define P 13 //取不大于m的最大质数,这里根据测试数据表使用13
typedef int keytype;
typedef struct link_node
{
keytype key;
struct link_node *next;
}L_node;
void input(int *arr,int *key,int len);
void input_test(int *arr,int *key,int len);
int H(int key);//哈希函数
void Set_Hash(L_node *HP[m],keytype key);//建立哈希表
L_node *Hash_search(L_node *HP[m],keytype key);//哈希查找
void Hash_insert(L_node *HP[m],L_node *new_node);//哈希插入
#endif