散列(哈希表)
1.基本思想:将关键字 (数据项)被映射到从0到Tablesize-1这个范围中的某个数,并且被放到适当的单元中。其中这个映射就叫做散列函数。
如图:
2.冲突:将两个不同的关键字映射到同一表的位置。如图:
3.构造哈希函数的两个标准:(1)简单并且能快速计算;(2)能在地址中获取键的均部分。
4.构造哈希函数的几种常见方法:
(1)平均取中法:具体做法是先通过关键字的平方值扩大相近数的差别,然后根据表长度取中间的几位数作为哈希值。
例如:将一组关键字(0100,0110,1010,1001,0111)平方后得(0010000,0012100,1020100,1002001,0012321)若取表长为1000,则可取中间的三位数作为散列地址集:(100121,201,020,123)。
(2)除余法:用关键字除以一个不大于哈希表长度m的正整数
(3)折叠法:根据哈希表长将关键字尽可能分成若干段,然后将这几段的值相加,并将最高位的进位舍去,所得结果即为其哈希地址。
例如:有一组关键字(4766934, 5656975, 4685673, 3547807,7569664),将这些数拆成2位,4位和1位数,然后再把它们相加,如下图:
4.解决哈希冲突
4.1开放定址法:当冲突发生时,按照某种方法探测表中的其他存储单元,直到找到空位置为止。
(1)线性探测法:di = 1,2,3,...,m−1
(2)二次探查法:di = 12,22,32,42,...,k2
(3)双重哈希法 : 一旦发生冲突, 应用第二个哈希函数以获取备用位置。
4.2链表法:将所有关键字为同义词的结点链接在同一个单链表中。
5.哈希查找的Java实现:
“`Java
/**
* 哈希查找的算法的实现
*
* {1,3,66,56,34,67,343,77,31,64,0,5,6,32,55,23,56}采用哈希表存放 用除余法构建哈希函数 用链表法解决哈希冲突
*
* @author zww
*
*/
public class HashTableSearch {
/* 哈希结点 */
private static class Node {
int key; // 链表中的键
Node next; // 下一个同义词
}
/* 在哈希表中查找关键字key */
public boolean HashSearch(int[] data, int key) {
int p = 1;
// 寻找小于或等于最接近表长的素数
for (int i = data.length; i > 1; i--) {
if (isPrimes(i)) {
p = i;
break;
}
}
// 构建哈希表
Node[] hashtable = createHashTable(data, p);
// 查找key是否在哈希表中
int k = key % p;
Node cur = hashtable[k];
while (cur != null && cur.key != key) {
cur = cur.next;
}
if (cur == null) {
return false;
} else {
return true;
}
}
/*用求余,链表法构建哈希表*/
public Node[] createHashTable(int[] data, int p) {
Node[] hashtable = new Node[p];
int k; //哈希函数计算的单元地址
for (int i = 0; i < data.length; i++) {
Node node = new Node();
node.key = data[i];
k = data[i] % p;
if (hashtable[k] == null) {
hashtable[k] = node;
}else {
Node current = hashtable[k];
while(current.next != null) {
current = current.next;
}
current.next = node;
}
}
return hashtable;
}
public boolean isPrimes(int n) {
for(int i = 2; i <= Math.sqrt(n); i++ ) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
“`