查找

查找(或检索)是在给定信息集上寻找特定信息元素的过程。

 

待查找的数据单位(或数据元素)称为记录,在学生管理系统中,一个学生的全部信息称为一条记录。如果这个学生的某个属性可以作为他的标识属性,称为关键字 key。如果这个key值可以最为学生的唯一标识,那么称为 主key。我们就是通过key值,从一堆数据中来检索我们想要的那条记录。称为查找

 

查找的方法

不同的应用场合适用不同的方法,查找方法有很多,有顺序查找、折半查找、分块查找、树表查找及Hash表查找等等。本次介绍

  • 顺序查找
  • 折半查找
  • Hash表查找

总结来说,对于无序的一组数据,通常也只能通过顺序查找,这种一一比较的方法简单粗暴,但如果数据过多的话,效率上就说不过去了。如果选用折半查找,需先对数据记录进行排序(通过主key值的大小),然后利用已排序的数据进行查找,效率较高。但有时数据库中的数据记录是不断变化的,不可能时时刻刻保持已排序的状态,所以有了Hash表排序。

 

顺序表查找

可以对无序记录表查找,但是效率是最低的,查找某记录几乎要扫描整个表,当表长n很大时,会令人无法忍受。
平均查找长度: ASL=O(n)。

算法思路:逐一比较,直到匹配记录输出

int seqsearch(int *a,int key)
{
	int i;
	for(i=N-1;i>=0;i--)
	{
		if(key==a[i])
			return i;
	}

	return -1;

}

 

折半查找

只能对有序记录表查找,效率相对较高。平均查找长度: ASL=O(log2(n+1)),大大优于O(n)。

算法思路:当记录的key按关系≤或≥有序时,则对给定值k,逐步确定待查记录所在区间,每次将搜索空间减少一半(折半),直到查找成功或失败为止。

int binsearch(int *a,int key)
{
	int low,high,mid;
	low=0;
	high=N-1;

	while(low<=high)
	{
		mid=(low+high)/2;//找到中间位置
		if(a[mid]==key)
			return mid;//查找成功,返回mid
		else if(key<a[mid])
			high=mid-1;//如果key<mid位置记录的key,则让high=mid-1;
		else 
			low=mid+1;//如果key>mid位置记录的key,则让low=mid+1;
	}

	return -1;//查找失败,返回-1

}

 

Hash表查找

Hash表,又称散列表、杂凑表。在前面讨论的顺序、折半、分块查找和树表的查找中,其ASL的量级在O(n)~O(log2n)之间

Hash表的查找特点:怎么构造的表就怎么查找,即造表与查找过程统一

 

Hash表的产生背景

之前的查找方法建立在比较的基础上,随着n的扩大,算法的效率会越来越低。这是因为记录的key与记录的存放地址无关,因而查找只能建立在key的“比较”基础上。
对给定的k,不经任何比较便能获取所需的记录,其查找的时间复杂度为常数级O(C)。这就要求在建立记录表的时候,确定记录的key与其存储地址之间的关系f,即使key与记录的存放地址H相对应

说白了就是记录按照key值存放,key值与地址有函数f的映射关系。这就是Hash表

 

关于Hash表的讨论关键是两个问题

  • 选取Hash函数的方法;
  • 确定解决冲突的方法。

选取(或构造)Hash函数的方法很多,原则是尽可能将记录均匀分布,以减少冲突现象的发生

hash函数构造方法:直接地址法,数字分析法,平方取中法,叠加法,保留除数法,随机函数法

处理冲突方法:开放地址法、链地址法

 

实例讲解

这里使用保留除数法构造hash函数,线性探查法处理冲突

 

算法思路:对给定k,根据造表时选取的H(key)求H(k)。若H(k)单元=^,则查找失败,否则k与该单元存放的key比较,若相等,则查找成功;若不等,则根据设定的处理冲突方法,找下一地址Hi,直到查找到或等于空为止。

 

程序实现

hash表数据结构

typedef int datatype;

typedef struct hashtbl{
	datatype *h;
	int length;
}hash_tbl,*hash_tp;

 

初始化hash表

申请hash表空间

void init_hash(hash_tp *Hp,int m)
{
	(*Hp)=(hash_tp)malloc(sizeof(hash_tbl));
	if(NULL==*Hp)
	{
		perror("malloc");
		exit(-1);
	}
	(*Hp)->length=m;
	(*Hp)->h=(datatype*)malloc((*Hp)->length*sizeof(datatype));
	if(NULL==(*Hp)->h)
	{
		perror("Malloc");
		exit(-1);
	}

	int i;
	for(i=0;i<(*Hp)->length;i++)
	{
		(*Hp)->h[i]=-1;
	}
}

 

创建hash表

int fun(int m)
{
	int i;
	for(;m>1;m--)
	{
		for(i=2;i<m;i++)
			if(m%i==0)
			{
				break;
			}
		if(i>=m)
			return m;
	}
	return -1;
}

void create_hash(hash_tp hp,datatype *a)
{
	int hash_val,i,p;
	p=fun(hp->length);
	if(p==-1)
		exit(-1);
	for(i=0;i<N;i++)
	{
		//1、用除留余数法构建hash函数
		hash_val=a[i]%p;
		//2、用线性探查法处理冲突
		while((hp->h[hash_val])!=-1)
			hash_val=(hash_val+1)%hp->length;
		//3、将记录存储在hash_val的位置
		hp->h[hash_val]=a[i];
	//	hash_show(hp);
	}
}

 

 

hash查找

int hash_search(hash_tp hp,int key)
{
	int hash_val,flag,p;
	
	p=fun(hp->length);
	hash_val = key %p;//用除留余数法得到hash地址

	while((hp->h[hash_val])!=key)
	{
		hash_val = (hash_val+1)%hp->length;
		flag++;
		if(flag==hp->length)
			return -1;
	}

	return hash_val;
}