哈希哈希

前言:
  哈希表(Hash Table)也叫散列表,是一种用于快速存取的数据结构。其内部实现是通过把键(key)码映射到表中的一个位置来访问记录,其中的“映射”也就是哈希函数,而“表”即哈希表。本文将重点介绍实现哈希表的2种方法:拉链法和线性探测法。

1.实验数据

A 2
C 1
B 6
B 11
H 1
J 3
数据说明:为了跟前面博文保持一致,本文的数据依然采用键值对(Key-Value)的形式。哈希表中主要有"存"和"取"操作。前者为:void put(Key key,Value);后者为: Value get(Key key);执行put操作时,如果数据已经存在则更新为新数据。

2.HashMap实现

  实现哈希表主要分以下两步:
step1:定义哈希函数
  哈希函数的实现不唯一,在此我们以java自带的hashCode()为基础进行修改。(对hashCode()底层实现感兴趣的朋友可另找资料进行了解~)

public int hash(Key key){
        return (key.hashCode() & 0x7fffffff)%M;
    }

step2:解决哈希冲突
  当出现 hash(k1)== hash(k2)时,即产生冲突。
解决方法一(拉链法):因为哈希值相等,我们可以将k1,k2利用链表 st 进行存储。即,凡是hash(x)相等的x都存入同一链表。当要进行查找操作时,先利用hash(x)定位到链表st[hash(x)],再通过顺序链表遍历进行查找。
解决方法二(线性探测法):利用两张足够大的哈希表 keys[M]和Values[M](其中,M>=2*N),当出现冲突之后,顺势循环向下遍历查找还没有存放 key 的keys[hash(key)],直到找到keys[hash(key)]==null为止。
------------

拉链法与线性探测法的存储比较:

3.Java代码实现
a.拉链法code:

 1 package com.gdufe.hash;
 2
 3 import java.io.File;
 4 import java.util.Scanner;
 5
 6 public class SequenceChain<Key,Value>{
 7
 8     private int N;
 9     private int M;
10     private SequentialSearch<Key, Value> []st;
11
12     public SequenceChain() {
13         this(997);
14     }
15     String s;
16     public SequenceChain(int M){
17         this.M = M;        //赋值M
18         st = (SequentialSearch<Key,Value>[]) new SequentialSearch[M];
19         for(int i=0;i<M;i++)
20             st[i] = new SequentialSearch();    //初始化
21     }
22
23     public int hash(Key key){    //自定义哈希,防止哈希值为负
24         return (key.hashCode() & 0x7fffffff) % M;
25     }
26     public void put(Key key,Value value){
27         st[hash(key)].put(key, value);
28     }
29     public Value get(Key key){
30         return st[hash(key)].get(key);
31     }
32
33     public static void main(String[] args) throws Exception{
34         Scanner input = new Scanner(new File("data_hash.txt"));
35         SequenceChain<String, Integer> sequence = new SequenceChain<String, Integer>();
36         while(input.hasNext()){
37             String key = input.next();
38             int value = input.nextInt();
39             sequence.put(key, value);
40         }
41         input.close();    //关闭控制台输入
42         System.out.println("-----output----");
43         System.out.println(sequence.get("H"));
44         System.out.println(sequence.get("B"));
45
46     }
47
48 }

辅助数据结构---顺序链表code:

 1 package com.gdufe.hash;
 2
 3 import java.io.File;
 4 import java.util.Scanner;
 5
 6 public class SequentialSearch<Key,Value> {
 7
 8     private Node root;    //顺序链表的根root
 9     class Node{
10         Key key;
11         Value value;
12         Node next;
13         public Node(Key key,Value value){
14             this.key=key;
15             this.value=value;
16         }
17     }
18     //在表头新增结点
19     public void put(Key key,Value value){
20         if(get(key)!=null){            //更新即可
21             for(Node x=root;x!=null;x=x.next){
22                 if(x.key.equals(key)){
23                     x.value=value;
24                     return;
25                 }
26             }
27         }
28         Node h = new Node(key,value);
29         h.next=root;
30         root = h;
31     }
32
33     public Value get(Key key){
34         for(Node x=root;x!=null;x=x.next){
35             if(x.key.equals(key)){
36                 return x.value;
37             }
38         }
39         return null;
40     }
41
42 }    

b.线性探测法code:

 1 package com.gdufe.hash;
 2
 3 import java.io.File;
 4 import java.util.Scanner;
 5
 6 public class LinearIndex<Key,Value> {
 7
 8     int N=0;
 9     int M=16;    //初始大小
10     Key[] keys;
11     Value[] values;
12
13     public LinearIndex() {
14         keys = (Key[]) new Object[M];   //强制转型
15         values = (Value[]) new Object[M];
16     }
17     public void put(Key key,Value value){
18         if(N*2>=M) resize();  //扩大哈希表
19         int i;
20         for(i=hash(key);keys[i]!=null;i=(i+1)%M){
21             if(keys[i].equals(key)){  //    更新value
22                 values[i]=value;
23                 return;
24             }
25         }
26         keys[i]=key;
27         values[i]=value;
28         N++;
29     }
30
31     private void resize() {        //扩大一倍
32         Key[] keyTemp = (Key[]) new Object[2*keys.length];
33         System.arraycopy(keys, 0, keyTemp, 0, keys.length);
34         keys = keyTemp;
35         Value[] valueTemp = (Value[]) new Object[2*values.length];
36         System.arraycopy(values, 0, valueTemp, 0, values.length);
37         values = valueTemp;
38
39         M = M*2;    //shouldn‘t be neglected!
40     }
41     public Value get(Key key){
42         for(int i=hash(key);keys[i]!=null;i=(i+1)%M){
43             if(keys[i].equals(key))
44                 return values[i];
45         }
46         return null;
47     }
48     public int hash(Key key){
49         return (key.hashCode() & 0x7fffffff)%M;
50     }
51
52     public static void main(String[] args) throws Exception {
53         Scanner input = new Scanner(new File("data_BST.txt"));
54         LinearIndex<String, Integer> li = new LinearIndex<String, Integer>();
55         while(input.hasNext()){
56             String key = input.next();
57             int value = input.nextInt();
58             li.put(key, value);
59         }
60         input.close();
61         System.out.println("-----output----");
62         System.out.println(li.get("H"));
63         System.out.println(li.get("B"));
64     }
65 }

4.测试结果

-----output----
1
11

注意:

  (1)Java底层实现HashMap采用的是上述介绍的拉链法。关于HashMap的底层实现在Java面试中是面试官喜欢问的问题之一。

  (2)关于拉链法采用的辅助结构为什么选择顺序链表而不采用高效的“二叉查找树“是因为,当哈希表较大而每张链表存储的数据不多时,顺序链表的效率反而更高一些。

结语:

  同之前介绍的红黑树一样,哈希表也是一种高效的存储于查找的数据结构,特别适用于大数据的场合。至于在何时使用哈希表何时使用红黑树这个不一而论。因为,存储的效率还更数据本身相关。不过,由于哈希一向擅长处理跟字符串相关的存储,所以对于大量的字符串存储与查找可以优先考虑哈希表。

时间: 2024-11-08 22:34:21

哈希哈希的相关文章

#哈希# ----- 哈希表初学

哈希表 哈希表是根据设定的哈希函数H(key)和处理冲突方法将一组关键字映射到一个有限的地址区间上,并以关键字在地址区间中的象作为记录在表中的存储位置,这种表称为哈希表或散列,所得存储位置称为哈希地址或散列地址.作为线性数据结构与表格和队列等相比,哈希表无疑是查找速度比较快的一种. 对不同的关键字可能得到同一散列地址,即k1≠k2,而f(k1)=f(k2),这种现象称为碰撞(英语:Collision).具有相同函数值的关键字对该散列函数来说称做同义词.综上所述,根据散列函数f(k)和处理碰撞的方

刘希彦&#183;到底该如何进补

http://mp.weixin.qq.com/s?__biz=MzA4MTkzNjczMg==&mid=2650972983&idx=1&sn=54063d5d81da77d5ff86e5e421603071&chksm=847b6b04b30ce2128581cde09214f6fe06e04d9c202ccb2b6d57625d68ca5e4823c95a051ee5&scene=0#rd 2017-03-18                         

哈希算法集锦

因为工作原因,看了一下redis的底层实现,发现redis底层使用的哈希算法是murmurhash,第一次听说这个算法感觉自己对哈希值计算的常用算法了解太少,整理了一下网上讲的比较原理性的观点: 简介   哈稀函数按照定义可以实现一个伪随机数生成器(PRNG),从这个角度可以得到一个公认的结论:哈希函数之间性能的比较可以通过比较其在伪随机生成方面的比较来衡量. 一些常用的分析技术,例如泊松分布可用于分析不同的哈希函数对不同的数据的碰撞率(collision rate).一般来说,对任意一类的数据

perl学习笔记——哈希

哈希 哈希是一种数据结构,它和数组的相似之处在于可以容难任意多的值并能按需取用,而他和数组的不同在于索引的方式,数组是以数字为索引而哈希则是以名字为索引. 哈希的键是唯一的,哈希的值可以重复. 哈希的应用场景 举例: 按名字找姓: 按主机名找IP地址或者按照IP地址找主机名: 按照单词统计其出现次数: 按用户名统计每个人使用的磁盘块数量: 按照驾照号码找出姓名. 访问哈希元素 如:$hash{$some_key} //注意使用的是大括号{} 不是中括号 赋值: $family_name{'fre

开地址哈希表(Hash Table)的原理描述与冲突解决

在开地址哈希表中,元素存放在表本身中.这对于某些依赖固定大小表的应用来说非常有用.因为不像链式哈希表在每个槽位上有一个"桶"来存储冲突的元素,所以开地址哈希表需要通过另一种方法来解决冲突. 解决冲突的方法 在开地址哈希表中,解决冲突的方法就是探查这个表,直到找到一个可以放置元素的槽. 例如,插入一个元素时,我们探查槽位直到找到一个空槽,然后将元素插入此槽中.删除或查找一个元素时,我们探查槽位直到定位到该元素或找到一个空槽.如果在找到元素之前找到一个空槽或遍历完所有槽位,那么说明此元素在

数据结构与算法实例(哈希表实现)

数据结构与算法(哈希表) 哈希函数:在记录的关键字与记录的存储地址之间建立的一 种对应关系叫哈希函数. 哈希函数是一种映象,是从关键字空间到存储地址空间的一 种映象.可写成:addressi=H(keyi) ,其中i是表中某 个元素. 哈希表:应用哈希函数,由记录的关键字确定记录在表中的 地址,并将记录放入此地址,这样构成的表叫哈希 ★哈希表的特点就是数据与其在表中的位置存在相关性,也就是有关系的,通过数据应该可以计算其位置,哈希表最大的特点是是可以快速实现查找,插入和删除.因为它独有的特点,H

NYOJ 2356: 哈希计划【模拟】

题目描述 众所周知,LLM的算法之所以菜,就是因为成天打游戏,最近LLM突然想玩<金庸群侠传X>,结果进去后各种被虐,LLM就开始研究这个游戏的代码,顺便还学会了一点点点点lua语言,然后就开始了伟大的改游戏代码之旅,然后LLM发现自己too young了,这个游戏把所有的文本都进行了哈希,如果自己改了代码或者剧情文本的话它哈希出来的值就会和原来的哈希值不一样......然后游戏就会打不开.....,现在LLM发现了文本的哈希函数,要求你写个程序,功能为: 输入一段字符串,输出一个哈希值 为了

7.哈希

哈希(Hash)又称散列,它是一个很常见的算法.在Java的HashMap数据结构中主要就利用了哈希.哈希算法包括了哈希函数和哈希表两部分.我们数组的特性可以知道,可以通过下标快速(O(1))的定位元素,同理在哈希表中我们可以通过键(哈希值)快速的定位某个值,这个哈希值的计算就是通过哈希函数(hash(key) = address )计算得出的.通过哈希值即能定位元素[address] = value,原理同数组类似. 最好的哈希函数当然是每个key值都能计算出唯一的哈希值,但往往可能存在不同的

哈希表

哈希表支持的一种最有效的检索方法:散列. 由于计算哈希值和在数组中进行索引都只消耗固定时间,因此哈希表的最大亮点在于他是一种运行时间在常量级的检索方法. 哈希表主要有两种: 1.链式哈希表:将数据存储在桶中的哈希表,每个桶里面都是一个链表,且链表的容量随着冲突的增大而增大.(换句话说就是如果有冲突,会在桶中的链表加上一个存储的值) 2.开地址哈希表:将数据存在表本身,而不是在桶中,他通过各种探查方法来避免冲突. 解决冲突: 不管在以上那种哈希表中,我们的目标是尽可能均匀的分配表中的元素.所以我们