哈希表的JAVA实现

来源:互联网 发布:java web访问静态资源 编辑:程序博客网 时间:2024/06/08 00:54

    哈希表作为一种数据结构,在百度百科上的定义为:散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。当时不理解这个概念是什么意思,查询了一些资料,可以简单地理解为:定义了一个表(广义上的储存数据的数据结构),根据自己定义的存放数据的函数,把这些数据放到各自的位置上。

    根据这个定义,就能解释定义中“直接访问”的含义,因为自己定义的函数,查询的时间复杂度几乎为O(1),这里说几乎是因为有些程序员可能根据情况的不同定义一些较为复杂的函数。但是相比于树,查询效率就大了很多。

    所以在这里,要建立一个哈希表,得有两个东西,第一个是哈希函数,用于存取数据的位置,第二个是储存数据的数据结构。

    在这里,因为“直接访问”这个特性的存在,所以在数据结构中只能选择顺序结构,于是就剩下数组和链表了。所以缺点也显而易见,第一是有可能数组或者链表的空的位置太多了,浪费资源,第二是在增大数组或者链表时较为麻烦。

    以下是比较常见的哈希函数(散列函数):

1. 直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。若其中H(key)中已经有值了,就往下一个找,直到H(key)中没有值了,就放进去。
2. 数字分析法:分析一组数据,比如一组员工的出生年月日,这时我们发现出生年月日的前几位数字大体相同,这样的话,出现冲突的几率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果用后面的数字来构成散列地址,则冲突的几率会明显降低。因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。
3. 平方取中法:当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。[2] 
例:我们把英文字母在字母表中的位置序号作为该英文字母的内部编码。例如K的内部编码为11,E的内部编码为05,Y的内部编码为25,A的内部编码为01, B的内部编码为02。由此组成关键字“KEYA”的内部代码为11052501,同理我们可以得到关键字“KYAB”、“AKEY”、“BKEY”的内部编码。之后对关键字进行平方运算后,取出第7到第9位作为该关键字哈希地址,如下图所示
关键字
内部编码
内部编码的平方值
H(k)关键字的哈希地址
KEYA
11050201
122157778355001
778
KYAB
11250102
126564795010404
795
AKEY
01110525
001233265775625
265
BKEY
02110525
004454315775625
315
[2] 
4. 折叠法:将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址。数位叠加可以有移位叠加和间界叠加两种方法。移位叠加是将分割后的每一部分的最低位对齐,然后相加;间界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加。
5. 随机数法:选择一随机函数,取关键字的随机值作为散列地址,通常用于关键字长度不同的场合。
6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。[3] 

    然而还有一个比较重要的东西,在放数据的时候,若是根据函数都放到了一个地方了怎么办?这就是处理冲突的方法,当然如果你自己定义的函数足够好,在数据量足够大的情况下还能保证每个都能分配到唯一的位置,那也是可以的,当然这在现实操作中几乎不可能办到。

    以下是常见的处理冲突的方法:

1. 开放寻址法:Hi=(H(key) + di) MOD m,i=1,2,…,k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:
1.1. di=1,2,3,…,m-1,称线性探测再散列;
1.2. di=1^2,-1^2,2^2,-2^2,⑶^2,…,±(k)^2,(k<=m/2)称二次探测再散列;
1.3. di=伪随机数序列,称伪随机探测再散列。
2. 再散列法:Hi=RHi(key),i=1,2,…,k RHi均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。
3. 链地址法(拉链法)
4. 建立一个公共溢出区

    好了,优缺点都说了,下面就放上我比较简易的哈希表建立过程。

    第一种是以数组为数据结构,函数为除留余数法,处理冲突的方法为开放寻址法。

    哈希函数:

    public int HashFunc(T t){//表长求余哈希函数    int pos;    pos=t.hashCode()%this.length;    return Math.abs(pos);    }
   哈希表构造方法:

   

    public HashTable(T[] table){//构造函数    int i=0;    int length=table.length;    this.table=(T[])new Object[this.length];    while(i<length){    T t=table[i];    int b=HashFunc(t);    if(this.di[b]!=0){    int j=1;    while(IsEmpty(di[b])){    b=(b+j)%this.length;    }    }    this.table[b]=table[i];di[b]++;i++;    }    }
private int[] di=new int[20];
private T[] table;
这里我定义了一个数组,然后一个是增量序列。

接下来是另外一种方式实现哈希表。

数据结构为链表,函数为字符长度,处理冲突的方法为拉链法。

申请了两个内部类。

<span style="white-space:pre"></span> class HeadNode<T> {<span style="white-space:pre"></span>    public Node next;<span style="white-space:pre"></span>    public HeadNode(){<span style="white-space:pre"></span>    <span style="white-space:pre"></span>this.next=null;<span style="white-space:pre"></span>    }    <span style="white-space:pre"></span>}<span style="white-space:pre"></span> class Node<T> {<span style="white-space:pre"></span>public Node(){<span style="white-space:pre"></span>this.data=null;<span style="white-space:pre"></span>this.next=null;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>public T data;<span style="white-space:pre"></span>public Node<T> next;}
private HeadNode[] head=new HeadNode[20];//申请20个头结点
构造函数:

    public HashLink(T t[]){    for(int j=0;j<20;j++)     {head[j]=new HeadNode<T>();}    int i=0;        Node<T> node1;    while(i<t.length){    Node<T> node=new Node<T>();    int c=HashFunc(t[i]);    node.data=t[i];    if(head[c].next==null) head[c].next=node; //如果头结点的NEXT为空的话,直接将NODE作为头结点的NEXT    else{  //否则就循环一直到结点为NULL后接上NODE结点    node1=head[c].next;    while(node1.next!=null)node1=node1.next;    node1.next=node;    }        i++;    }    }
哈希函数:
    public int HashFunc(T t){//以字符长度座位哈希函数    int pos;    pos=t.toString().length()%this.length;    return Math.abs(pos);    }
输出函数:

        public void Output(){     Node<T> node =new Node<T>();    for(int i=0;i<20;i++){    if(head[i].next!=null) {node=head[i].next;    while(node!=null){    System.out.print(node.data+" ");    node=node.next;    }    System.out.println();    }    }    }
插入函数:

    public void Insert(T t){    Node<T> node=new Node<T>();    Node<T> node1;int c=HashFunc(t);node.data=t;if(head[c].next==null) head[c].next=node;else{node1=head[c].next;while(node1.next!=null)node1=node1.next;node1.next=node;}    }
获取位置函数:

    public int GetPosition(T t){    int c=HashFunc(t);    Node<T> node =new Node<T>();    if(head[c].next!=null){    node=head[c].next;    while(node!=null){    if(node.data.equals(t))return c;    node=node.next;    }    }     return -1;    }

主函数测试:
public class Mainn { public static void main(String[] args){ String str[]={"int","String","youf","float","duetous","cao"}; HashLink<String> h=new HashLink<String>(str); h.Output(); System.out.println(h.GetPosition("cao")); System.out.println(h.GetPosition("weaa"));  }}

结果:



反思:写链表的时候出现了不少问题。

第一个是在定义内部类的时候,我定义的类是public class HashLink<T>  ,因为有T这个不定类型的存在,在内部类的时候也得加上这个不然在后面会出错。

第二个是我在调试的过程中总是出现nullpointerexception 这个异常,主要是因为我在创建的时候,不是说不能赋予NULL值或者以NULL值来判断,是因为就根本不存在某个结点,才会出现这种情况。比如说在head判断的时候 只能判断head.next为不为null值,不能判断head为不为null值,因为当时申请了head结点的空间所以不可能为NULL值。这里的nullpointerexception 和NULL值没有关系,和存在不存在有关系。

在输出函数中,判断是用node!=null,这里可以用的原因是因为,在不断地node=node.next;以后,会出现没有申请空间的结点。而之前如果用Head来判断的话,那么在后面,node1=head[c].next;就会出现问题,这里还不会出现问题,主要在node1.next!=null 因为node1已经是NULL了,没有申请空间,也就没有next这一选项

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 我的中国银行卡在异地被冻结怎么办 qq密保号码换了验证不了怎么办 qq登陆需要密保手机验证怎么办 微信漂流瓶被投诉收不到回复怎么办 电视的屏幕出现三条黑线怎么办 付了定金的房子不想要了怎么办 顾客刚买了东西就退怎么办 飞机票买了但是没有报团怎么办 日本寄东西回来深圳海关扣了怎么办 xp框架安装模块后无限重启怎么办 手机百度云下载的文件不见了怎么办 百度云手机号换了文件不见了怎么办 好劵app提现密码忘了要怎么办 微信上买的衣服不给退货怎么办 在微信上买的衣服不给退货怎么办 贴贴纸的地方出现色差了怎么办 贴在玻璃上的贴纸撕掉后有胶怎么办 魅族手机部分时间上划失效怎么办 别人电脑上c盘文件无权访问怎么办 网络已连接但上不了网怎么办 asp复选框选中的有重复值怎么办 邮件在邮递中出现损坏怎么办呢? 爱彩彩票资金密码忘记了怎么办 注册彩票网站忘了资金密码怎么办 电车电瓶加水后电压变低怎么办 电动车电瓶四块电池坏了一块怎么办 把小孩的玩具修坏了怎么办 电动车插头太紧了插不到底怎么办 宜家沙发不能更换布套怎么办 苹果官网买的无线充坏了怎么办 苹果官网上买的耳机坏了怎么办 led灯开关关掉了闪烁是怎么办 我朋友借的网贷光给我打电话怎么办 电脑上的文件变成了图片查看怎么办 外卖店打印机纸卡住打不开了怎么办 微信撤回图片留下一堆代码怎么办 拼多多砍价免费拿砍不到0元怎么办 一岁三个月宝宝老足拉肚子怎么办丶 想打好关系送礼发红包不敢收怎么办 我在相亲市场太受欢迎了怎么办小说 有人报警说我诈骗警察说立案怎么办