一、概述
这是哈希的一种算法,算是冲突比较少的,但是也难免会有。
这是我们需要探究的问题,开链法就是好方法之一。
开链法原理比较简单,代码比较玄学,大家要在学习的过程之中动手模拟,才能完全掌握它。
int hash(string h)
{
int seed=37,p=10007;
int ans=0;
int len=h.length();
for(int i=0;i<n;i++)
{
ans=(ans*seed+h[i]-'0')%p;
}
return ans;
}
二、原理分析
结构假如要用形象来表示,就如下图所示:
假如说我们要对以下一些数据进行哈希储存(%53):
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
0 | 3 | 51 | 53 | 55 | 105 | 106 | 2 | 52 |
0 | 3 | 51 | 0 | 2 | 52 | 0 | 2 | 52 |
*第一行是编号,第二行是原始数据,第三行是哈希后结果。
按开链法储存之后如图所示:
三、代码讲解
原理十分简明,而代码却不好实现,需要大家细细揣摩。
操作时代码从下向上看的!
扫描二维码关注公众号,回复:
12397831 查看本文章
首先,关于hash的大多数程序实现都离不开两个数组:
first[i]:表示哈希值为i的第一个数的下标(用来记录第i列最末的数据是第几个输入的)
next[i]:表示第i个数据的下一个数据(用来记录第i个数的上方)
就拿第一列来说吧
当读入0时 first[0]=1;next1]=0;
当读入53时 first[0]=2;next[2]=1;
当读入106时 first[0]=3;next[3]=2;
比如说我要进行查找,会从first[hash(h)]开始,逐个向上查找。
四、程序设计
对于一个程序来说,有一些基本操作是需要掌握的,难度主要是while的操作和两个数组的相关性。
部分代码个人感觉为玄学操作,建议带入模拟一下。
给个哈希简单代码,之后的程序围绕它来展开:
int hash(string h)
{
int seed=37,p=10007;
int ans=0;
int len=h.length();
for(int i=0;i<n;i++)
{
ans=(ans*seed+h[i]-'0')%p;
}
return ans;
}
1、插入
尤其注意两个数之间的关系,建议带数据模拟!
int add(string h)
{
int sum=hash(h);//用sum来记录该数据所存的数组
int u=first[sum];//这个主要用于下一步的去重
while(u)//这一步用来去重,原理是借组next数组枚举查找,这个while是比较巧妙的写法
{
if(data[u]==h)//一模一样
return 0;//返回
u=next[u];//使用next数组接着往下(也就是上)找
}
++cnt;//这是添加的第几个数据
data[cnt]=h;//将原始数据保存在data数组中,也就是在for循环时就直接对输入进行操作
//下面两步个人认为有点链式结构的感觉 ,难点就是这里
next[cnt]=first[sum];//第cnt个数据的下(也就是上)一个数据是第几个cnt
first[sum]=cnt;//更新本数列最下端的数为第几cnt
return 1;//成功添加
}
2、查找
插入的翻版,对while判定的改变。
int find(string h)
{
int sum=hash(h);//同上
int u=hash[sum];//同上
while(u)//注意改变
{
if(data[u]==h)//如果有
return 1;//ture
u=next[u];//接着找
}
return 0;//都没有就为假
}
3、删除
本板块的难点在pre上。
void delete(string h)
{
int sum=hash(h);//找到它的列
int pre,u;//pre的作用是判断删除的是不是头结点,u还是记录它上面的值
pre=u=first[sum];//记录
while(u)
{
if(data[u]==h) break;//找到了这个数据,往下走
//这两步可以使u比pre早一个版本
pre=u;
u=next[u];
}
if(pre==u) first[sum]=next[u];//删除的节点是头结点,队列末尾位次编号修改一下
else next[pre]=next[u];//删除的节点不是头结点 ,链表中捡摘除一部分后重新链接
}