哈希表:理想情况下可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函 数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快 找到该元素。
- 向哈希表中插入元素,根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放。
向哈希表中对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若 关键码相等,则搜索成功
但是当两个数通过哈希函数所编的关键码相同时,将这两个数想放入一个哈希表中的时候就会发生哈希冲突。而教材上一般这样说:不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
那木我们哈希表中解决这个冲突的方法也被称为冲突避免。
而我们需要明确一点,由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一 个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率。
而冲突避免方法的实现的原则一般有三个
- 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1 之间
- 哈希函数计算出来的地址能均匀分布在整个空间中
- 哈希函数应该比较简单
我们这里只说一种哈希函数
除留余数法:设散列表中允许的地址数为m,取一个不大于m,但接近或者等于m的质数p作为除数,按照哈希函数 Hash(key) = key% p(p<=m),将关键码转换成哈希地址。
解决哈希冲突的方法一般有:闭散列和开散列两种方法。
闭散列
- 闭散列也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以 把key存放到冲突位置中的“下一个” 空位置中去
闭散列的实现
- 通过哈希函数获取待插入元素在哈希表中的位置
- 如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到 下一个空位置,插入新元素,也就是说我们找这个数组中下一个没有被占用的位置
开散列的实现
- 开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子 集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
- 也就是说每一个数组元素都是一个节点,不同的关键字得到相同的哈希地址时其实得到了相同的节点值,若是这个节点已经被占用,则这写些不同的关键字则构成了一个链表。每次查找的时候就在根据哈希函数找到这个链表的头节点,然后开始查找。
我们来实现一下开散列的方法
public class Test {
static class Node {
public int key;
public int val;
public Node next;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
private Node[] array = new Node[101];
private int size = 0;
private int hasFun(int key) {
return key % array.length;
}
//插入
public void put(int key, int val) {
int index = hasFun(key);
Node head = array[index];
for (Node cur = head; cur != null; cur = cur.next) {
if (cur.key == key) {
cur.val = val;
return;
}
}
Node newNode = new Node(key,val);
newNode.next = head;
array[index] = newNode;
size++;
}
//寻找
public Node find(int key) {
int index = hasFun(key);
Node head = array[index];
for (Node cur = head; cur != null ; cur = cur.next) {
if (cur.key == key) {
return cur;
}
}
return null;
}
//删除
public void remove (int key) {
int index = hasFun(key);
Node head = array[index];
if (head.key == key) {
array[index] = head.next;
return;
}
for (Node prev = head; prev.next != null; prev = prev.next) {
if (prev.next.key == key) {
prev.next = prev.next.next;
}
}
}
}