标签:
散列表是一种用于以常数平均时间执行插入、删除、查找的算法。
散列表每个关键字被映射到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