查找3—散列表的查找

拜拜、爱过 提交于 2019-12-11 11:56:08

前言:

     这是查找这个知识点的最后一个部分,个人感觉这部分真的很好理解,就简单的逻辑思维,不需要绕弯,基本上就可以解决大部分的题目。合理的应用查找的总体效率也是很高的。主要说三个模块的事情:构造方法处理冲突方法查找

     那么引入一下,前面我们说了顺序表的查找、树表的查找,但是我们发现,就算再优化,它们的时间复杂度最小也是log2n级别的。那么我们肯定是希望能找到更优的算法,最好就是n(1)级别的,如果能利用公式直接的求解,那么不是直接就可以找到了,不需要依次比较了吗?所以就有了我们要说的散列表的查找(也叫哈希表)。

一、构造方法

     来看看基本的概念:

     散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

     构造方法有很多,数字分析法平方取中法折叠法除留余数法(这个是最常用的)。只需要思考一下就知道我们构造其实就是制定一个规则,使我们的元素可以按照这个规则查找到,当然,我们还可以有其他的方法(例如:你自己定义一个高等数学的计算公式,结构不一样,依旧可以映射到散列地址上)。

1、除留余数法

     这个是最常用的,理解也很简单。假设散列表表长为m,选择一个不大于m的数p(这里我们可以思考,最好的p应该是小于等于m的质数),用p去除关键字,除后得到余数为散列地址,即
H(key) = key%p



二、处理冲突的方法

      聪明的你会发现,用上述方法肯定会出现冲突,什么是冲突呢,就是我们两个元素根据散列函数计算得到的散列地址是一样的,这个时候怎么办呢?那么肯定需要一个统一的原则去处理这样的冲突,于是我们就有了:开放地址法链地址法去处理。
     

1、开放地址法:

     基本思想就是冲突的时候以这个冲突元素的地址再求下一个地址,以此类推,知道不冲突为止
1)线性探测法:
这个是最常用的,很好理解,就是当发生冲突的时候,我们都统一向前加一(或者向后加一),知道不冲突结束。
2)二次探测法:
其实和上述的一样,但是加减的规则边了,这里的加减步长不再是线性探测法的±1,而是d=12,-12,22,-22,…
3)伪随机探测法:
这个也很好理解,就是随便一个随机值(例如9,而我们之前的p=11),当我们冲突的时候,它的计算方式就是(9+冲突地址)%11,得到新的地址。
说到这里,你一定有很多其他的解决冲突的方式(现实应用的时候其实我们规定了很多的限制去减少这样的冲突)。

2、链地址法

      链地址法的基本思想是:把具有相同散列地址的记录放在同一个单列表中,成为同义词链表。这样我们冲突的时候,不就直接往链表查找就可以了吗?



三、查找

既然你理解了上面的两个部分,那么查找对你来说也一定很容易
【算法描述】
1.给待查找的关键字key,根据造表时设定的散列函数计算H0=H(key);
2.若单元H0为空,则所查找元素不存在;
3.若单元H0中元素的关键字为key,则查找成功;
4.否则重复解决冲突的过程(这个方法很多,给出的代码演示里面使用的冲突解决方法是线性探测法)。

代码演示:

#include<iostream>
using namespace std;

//算法7.10 哈希表的查找
//- - - - -开放地址法哈希表的存储表示- - - - -
#define m 16                        			//哈希表的表长
#define NULLKEY 0                 			//单元为空的标记

struct HashTable{
   int  key;		         	 			//关键字项
// InfoType  otherinfo;					//其他数据项
};

//	算法7.10为哈希表查找的算法,采用线性探测法处理冲突。
//	【算法实现】

int H(int key)
{
	int result;
	result=key%13;
	return result;
} 

int SearchHash(HashTable HT[],int key){
  //在哈希表HT中查找关键字为key的元素,若查找成功,返回哈希表的单元标号,否则返回-1 
  int H0=H(key);     	                   			//根据哈希函数H(key)计算哈希地址
  int Hi;
  if (HT[H0].key==NULLKEY) return -1;		//若单元H0为空,则所查元素不存在
  else if (HT[H0].key==key) return H0;		//若单元H0中元素的关键字为key,则查找成功
  else{             
     for(int i=1;i<m;++i){                
		 Hi=(H0+i)%m;     		//按照线性探测法计算下一个哈希地址Hi
        if (HT[Hi].key==NULLKEY) return -1;	//若单元Hi为空,则所查元素不存在
        else if (HT[Hi].key==key) return Hi; 	//若单元Hi中元素的关键字为key,则查找成功
     }//for
     return -1;
  }//else
}//SearchHash

void main()
{	
	int result;
	int a[16]={-1,14,1,68,27,55,19,20,84,79,23,11,10,-1.-1.-1};
	HashTable HT[m];
	for(int i=0;i<16;i++)
	{
		HT[i].key=a[i];
	}
	result=SearchHash(HT,55);
	if(result!=-1)
	{
		cout<<"在第"<<result<<"位置找到"<<endl;
	}
	else
	{
		cout<<"未找到"<<endl;
	}
}

后记:

     *查找部分就完成了,这一系列的操作下来我们其实都是在逐步的找最优的算法,时间最优亦或空间最优。那么下一次应该就是和查找关联的排序了。*有误可以评论指出哦,谢谢。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!