哈希表

哈希表是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。

有两种不同类型的哈希表:哈希集合和哈希映射。

哈希集合是Set数据结构的实现之一,用于存储非重复值。
哈希映射是Map数据结构的实现之一,用于存储(key, value)键值对。

 

1.设计哈希表

1.1 哈希表的原理

哈希表的关键思想是使用哈希函数将键映射到存储桶。

1.当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
2.当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。

例如下边一个例子,哈希函数是: y%5

1.2 关键点

通过上边的介绍,可以得出,设计哈希表的两个关键点是 哈希函数 和 冲突解决。

上边介绍的例子, 以 y = x % 5 作为散列函数,其中 x 是键值,y 是分配的桶的索引。

1.2.1 散列函数:

散列函数的设计将取决于键值的范围和桶的数量。 

下面是一些有代表性的哈希函数示例:

 

哈希函数的设计是一个很开发而且复杂的事情,其思想是尽可能将键分配到桶中,理想情况下,完美的哈希函数将是键和桶之间的一对一映射。然而,在大多数情况下,哈希函数并不完美,它需要在桶的数量和桶的容量之间进行权衡。

 

1.2.2 冲突解决:

理想情况下,如果我们的哈希函数是完美的一对一映射,我们将不需要处理冲突。不幸的是,在大多数情况下,冲突几乎是不可避免的。例如,在我们之前的哈希函数(y = x % 5)中,1987 和 2 都分配给了桶 2,这是一个冲突。

冲突解决算法应该解决以下几个问题:

  1. 如何组织在同一个桶中的值?
  2. 如果为同一个桶分配了太多的值,该怎么办?
  3. 如何在特定的桶中搜索目标值?

根据我们的哈希函数,这些问题与桶的容量和可能映射到同一个桶的键的数目有关。

让我们假设存储最大键数的桶有 N 个键。

通常,如果 N 是常数且很小,我们可以简单地使用一个数组将键存储在同一个桶中。如果 N 是可变的或很大,我们可能需要使用高度平衡的二叉树来代替.。

 

1.3 实现哈希集合和哈希映射:

不使用高级的数据结构,实现一个哈希集合,要求在注释中标明。

 1 package leet_pro.ds;
 2 
 3 /**
 4  * @Title:MyHashSet
 5  * @Description:
 6  *
 7  * 不使用任何内建的哈希表库设计一个哈希集合,具体地说,你的设计应该包含以下的功能:
 8  * add(value):向哈希集合中插入一个值。
 9  * contains(value) :返回哈希集合中是否存在这个值。
10  * remove(value):将给定值从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。
11  *
12  * 注意:
13  * 所有的值都在 [0, 1000000]的范围内。  (值范围)
14  * 操作的总数目在[1, 10000]范围内。     (桶范围)
15  * 不要使用内建的哈希集合库。
16  *
17  * 示例:
18  * MyHashSet hashSet = new MyHashSet();
19  * hashSet.add(1);
20  * hashSet.add(2);
21  * hashSet.contains(1);    // 返回 true
22  * hashSet.contains(3);    // 返回 false (未找到)
23  * hashSet.add(2);
24  * hashSet.contains(2);    // 返回 true
25  * hashSet.remove(2);
26  * hashSet.contains(2);    // 返回  false (已经被删除)
27  *
28  * @Author: natty
29  * @Version: 1.0
30  * @Date: 2020-02-07 00:14
31  */
32 class MyHashSet {
33     private int bucketSize;   //桶数
34     private int size;         //桶容量
35     private int[][] array;    //存储数据结构,二维数组
36     private boolean flag;     //数值0不存储,flag为了判定0是否存在
37 
38     /** Initialize your data structure here. */
39     public MyHashSet() {
40         bucketSize = 10000;  //桶数量
41         size = 100;       //桶容量
42         array=new int[100][10000];  //数据存储结构
43         flag =false;
44     }
45 
46     public void add(int key) {
47         if(key==0){
48             flag=true;
49             return;
50         }
51         key = key - 1;      //防止1000000存在在 array[100][0]而发生越界,前挪到array[99][9999]存储。
52         int bucket1=key/bucketSize;
53         int bucket2=key%bucketSize;
54         array[bucket1][bucket2]=key+1;
55     }
56 
57     public void remove(int key) {
58         if(key==0){
59             flag=false;
60             return;
61         }
62         key=key-1;
63         int bucket1=key/bucketSize;
64         int bucket2=key%bucketSize;
65         array[bucket1][bucket2]=0;
66     }
67 
68     /** Returns true if this set contains the specified element */
69     public boolean contains(int key) {
70         if(key==0){
71             return flag;
72         }
73         key=key-1;
74         int bucket1=key/bucketSize;
75         int bucket2=key%bucketSize;
76         return key+1==array[bucket1][bucket2];
77     }
78 
79 }
View Code

 

posted @ 2020-02-06 19:31  葛洪俊  阅读(157)  评论(0)    收藏  举报