标签:
散列表是一种用于以常数平均时间执行插入、删除、查找的算法。
散列表每个关键字被映射到0到TableSize-1这个范围中的某个值,这个映射叫做散列函数。因为单元个数是有限的,两个关键字可能映射到同一个值,这个时候就需要通过一些方式来处理冲突。
以关键字为字符串为例,设计简单的散列函数。
通过将字符串的ASCII码值的和来计算hash值。
public static int hash1(String key, int tablesize) {
int hashVal = 0;
for (int i = 0; i < key.length(); i++) {
hashVal += key.charAt(i);
}
return hashVal % tablesize;
}较好的散列方法
public static int hash2(String key, int tablesize) {
int hashVal = 0;
for (int i = 0; i < key.length(); i++) {
hashVal = 37 * hashVal + key.charAt(i);
}
hashVal = hashVal % tablesize;
if (hashVal < 0) {
hashVal += tablesize;
}
return hashVal;
}
思想:将散列到同一个值的元素保留到一个链表中。
缺点:给新链表单元分配地址空间需要花费时间,会降低算法的速度。
分析:一次不成功的查找需要访问的节点数平均为;成功的查找需要遍历大约1+/2个链。
简单实现
public class SeparateChainHashTable<T> {
private static final int DEFAULT_SIZE = 100;
protected LinkedList[] lists;
public SeparateChainHashTable() {
this(DEFAULT_SIZE);
}
public SeparateChainHashTable(int size) {
lists = new LinkedList[PrimeNumber.nextPrime(size)];
for (int i = 0; i < lists.length; i++) {
lists[i] = new LinkedList<T>();
}
}
public boolean contains(T x) {
return lists[hashCode(x)].contains(x);
}
public void insert(T x) {
LinkedList<T> linkedList = lists[hashCode(x)];
if (!linkedList.contains(x)) {
linkedList.add(x);
}
}
public void remove(T x) {
LinkedList<T> linkedList = lists[hashCode(x)];
if (linkedList.contains(x)) {
linkedList.remove(x);
}
}
/**
* 计算hash值
*
* @param x
* @return
*/
private int hashCode(T x) {
int hashVal = x.hashCode();
hashVal = hashVal % lists.length;
if (hashVal > 0) {
hashVal += lists.length;
}
return hashVal;
}
}
1)当表满到一半时;
2)遇到插入失败时;
3)当表到达一个装填因子时(途中策略)。
通常选取一个好的截止点来采用第三种方案。
标准库中包括Set和Map的散列实现,即HashSet类和HashMap类。HashSet的实现直接借助了HashMap。JDK中是使用分离链接散列实现的。
欢迎扫描二维码,关注公众号
标签:
原文地址:http://blog.csdn.net/robertcpp/article/details/51863465