编辑文章 - 博客频道 - CSDN.NET

     哈希表存储的基本思想是:以数据表中的每个记录的关键字k为自变量,通过一种函数H(k)计算出函数值。把这个值解释为一块连续存储空间(即数组空间)的单元地址(即下标),将该记录存储到这个单元中。在此称该函数H为哈希函数散列函数。按这种方法建立的表称为哈希表散列表

构造好的哈希函数的方法,应能使冲突尽可能地少,因而应具有较好的随机性。这样可使一组关键字的散列地址均匀地分布在整个地址空间。根据关键字的结构和分布的不同,可构造出许多不同的哈希函数。

1.直接定址法 

      H(k)=k+c  (c≥0)

2.除留余数法

      H(k)=k%m    

3.平方取中法

4.折叠法

5.数值分析法

假设哈希表的地址范围为0m-l,当对给定的关键字k,由哈希函数H(k)算出的哈希地址为i(0≤i≤m-1)的位置上已存有记录,这种情况就是冲突现象

    处理冲突就是为该关键字的记录找到另一个“空”的哈希地址。即通过一个新的哈希函数得到一个新的哈希地址。如果仍然发生冲突,则再求下一个,依次类推。直至新的哈希地址不再发生冲突为止。

    常用的处理冲突的方法有 开放地址法链地址法两大类

1.开放定址法

   用开放定址法处理冲突就是当冲突发生时,形成一个地址序列。沿着这个序列逐个探测,直到找出一个“空”的开放地址。将发生冲突的关键字值存放到该地址中去。

   如  Hi=(H(k)+d(i)) % m, i=1,2,k   (k<m-1)

   其中H(k)为哈希函数,m为哈希表长,d为增量函数,d(i)=dl,d2…dn-l。

   增量序列的取法不同,可得到不同的开放地址处理冲突探测方法。

(1)线性探测法

    线性探测法是从发生冲突的地址(设为d)开始,依次探查d+l,d+2,…m-1(当达到表尾m-1时,又从0开始探查)等地址,直到找到一个空闲位置来存放冲突处的关键 字。

    若整个地址都找遍仍无空地址,则产生溢出。

    线性探查法的数学递推描述公式为:

           d0=H(k)

                   di=(di-1+1)% m   (1≤i≤m-1)

(2)平方探查法

   设发生冲突的地址为d,则平方探查法的探查序列为:d+12,d+22,…直到找到一个空闲位置为止。

平方探查法的数学描述公式为:

      d0=H(k)

      di=(d0+i2) % m  (1≤i≤m-1)

2.链地址法

         用链地址法解决冲突的方法是:把所有关键字为同义词的记录存储在一个线性链表中,这个链表称为同义词链表。并将这些链表的表头指针放在数组中(下标从0到 m-1)。这类似于图中的邻接表和树中孩子链表的结构。



   哈希法是利用关键字进行计算后直接求出存储地址的。当哈希函数能得到均匀的地址分布时,不需要进行任何比较就可以直接找到所要查的记录。但实际上不可能完全避免冲突,因此查找时还需要进行探测比较。

在哈希表中,虽然冲突很难避免,但发生冲突的可能性却有大有小。这主要与三个因素有关。

第一:与装填因子有关

第二:与所构造的哈希函数有关

第三:与解决冲突的哈希冲突函数有关


哈希表的题目,有一个共同点,那就是点的顺序不影响题目,但是

了解完了哈希表基本内容之后,我来推荐两道题目。

http://cqsyz.openjudge.cn/haixbiao/01/正方形

http://cqsyz.openjudge.cn/haixbiao/02/sumset

题目我就不复制过来了,看了这两个题目,很容易发现其中的共同点。就是已知给定数据中的两个过后,通过查找哈希表内是否有另外两个元素能使等式成立。

对于正方形一题,确定了一条边,就能找出整个正方形,当然,一条边可以对应两个正方形。通过一条边计算另外两个点的方法是什么呢?静下来思考一下,新的横坐标为横坐标之一加上纵坐标之差,同理,新的纵坐标等于原来纵坐标之一加上横坐标之差。如下。

int x1,x2,y1,y2,delx,dely;
delx=poi[i].x-poi[j].x;
dely=poi[i].y-poi[j].y;

第一组
x1=poi[i].x+dely; y1=poi[i].y-delx;
x2=poi[j].x+dely; y2=poi[j].y-delx;

第二组
x1=poi[i].x-dely; y1=poi[i].y+delx;
x2=poi[j].x-dely; y2=poi[j].y+delx;
此题如果不使用哈希表,判断另外两点是否存在只能用二维布尔数组,然后根据题目给我数据范围,20001,使用bool b[20001][20001]来判断,很遗憾,memset对于这么大的数组的负数坐标无能为力。b[-1][-1]=72。如此,考虑哈希表。

用哈希表的第一步是确定对什么数据进行哈希,很明显,这道题是bool数组,第二步是考虑如何哈希,这道题,我们想要的是二维变一维,并且尽可能的让哈希表填满,所以我采用让一维哈希表的坐标p=x*x+y*y来mo一个数,坐标确定了,那么值呢?注意了,哈希表的值只能等于一个数字,而判断的点确是一个坐标,如何建立其中的联系呢?让每一个点都有一个独特的值。于是可以x*10000+y,让x乘的数尽量大,保证唯一性。下面给出了建立哈希表的代码

void hash(int x,int y){
int position=(x*x+y*y)%30000;
long long zhi=x*100000+y;
while(1){
position+=position%31+1;
if(position>MAX) position%=MAX;
if(hashtable[position]==zhi) return;
if(hashtable[position]==0) break;
}hashtable[position]=zhi;
}

当手中有了哈希表,如何根据坐标来判断某点是否存在呢?我们拥有的这个哈希表需要达成的是bool效果,就把现在需要验证的坐标进行与建立时相同的运算,看这个坐标能否得到要的值。

int Find(int x,int y){
int position=(x*x+y*y)%30000;
long long zhi=x*100000+y;
while(1){
position+=position%31+1;
if(position>MAX) position%=MAX;
if(hashtable[position]==0) return 0;
else if(hashtable[position]==zhi) return 1;
}
}

分析完毕。

第二题sumset

首先找到a+b=d-c这个式子,意思是a,b的关系和d,c的关系对应,于是可以通过哈希表存储a+b的值,再两重循环枚举d,c如果哈希表里对应有值,就寻找最大的d

后面的过程和正方形几乎一样,不再分析。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值