散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
一个通俗的例子是,为了查找电话簿中某人的号码,可以创建一个按照人名首字母顺序排列的表(即建立人名x到首字母F(x)的一个函数关系)该函数就是一个哈希函数,在首字母为W的表中查找“王”姓的电话号码,显然比直接查找就要快得多。这里使用人名作为关键字,“取首字母”是这个例子中散列函数的函数法则F(),存放首字母的表对应散列表。关键字和函数法则理论上可以任意确定。
散列的特点:
1.查找速度快,很快,删除速度快
2.结构多,最流行的就是顺序表+哈希表的结果
哈希函数到底是什么?
哈希函数说到底就是散列函数,哈希表原来里边m个位置,每个位置都有一个编号,也叫索引,现在我要新放一个元素进去表中的位置,而哈希函数呢,就是将我们要放的这个元素进行处理取样,给我们返回一个信息,这个信息就是去指定位置的索引。比较经典的哈希函数就是HashCode();
有哪些解决冲突的方法?
1.用到链表的
- 分离链接法
2.不用到链表的
- 线性探测法
- 平方探测法
- 双散列法
- 再散列法
今天看看分离链接法
将散列到同一个值得所有元素保留到一个表中。
基本思想是采用N个链表组成链表数组,N为哈希表的长度
直接上代码
public class MySeparateHashTable<AnyType> {
// 默认哈希表大小
private static final int DEFAULT_TABLE_SIZE = 101;
// 每一个地址对应的链表,用一个list封装一下
private LinkedList[] theLists;
private int currentSize;
/**
* 默认的无参构造,构造指定大小的哈希表
*/
public MySeparateHashTable() {
this(DEFAULT_TABLE_SIZE);
}
/**
* 有参构造,并实例化每个地址对应的链表
* @param size
*/
public MySeparateHashTable(int size) {
theLists = new LinkedList[nextPrime(size)];
for(int i=0;i<theLists.length;i++) {
theLists[i] = new LinkedList<>();
}
}
/**
* 获取size的下一个素数
* @param n
* @return
*/
private static int nextPrime(int n) {
if( n % 2 == 0 )
n++;
for( ; !isPrime( n ); n += 2 );
return n;
}
/**
* 判断是否是素数
* @param n
* @return
*/
private static boolean isPrime(int n) {
if( n == 2 || n == 3 )
return true;
if( n == 1 || n % 2 == 0 )
return false;
for( int i = 3; i * i <= n; i += 2 )
if( n % i == 0 )
return false;
return true;
}
/**
* 清空哈希表
*/
public void makeEmpty() {
for(int i=0;i<theLists.length;i++) {
theLists[i].clear();
}
currentSize = 0;
}
/**
* 哈希函数
* @param key
* @param tableSize
* @return
*/
public int hash3(AnyType key) {
int hashVal = key.hashCode();
hashVal = hashVal % theLists.length;
if(hashVal < 0) {
hashVal = hashVal + theLists.length;
}
return hashVal;
}
/**
* 在哈希表中查找相关key
* @param key
* @return
*/
public boolean contatin(AnyType key) {
// 哈希表中存放相关key的链表
LinkedList list = theLists[hash3(key)];
//再调用链表的方法判断是否在此链表中
return list.contains(key);
}
/**
* 哈希表扩容
*/
public void refesh() {
//扩容
}
public boolean insert(AnyType key) {
// 哈希表中存放相关key的链表
LinkedList list = theLists[hash3(key)];
if(!list.contains(key)) {
list.add(key);
// 判断是否需要扩容
if(++currentSize > theLists.length) {
refesh();
}
return true;
}
return false;
}
public boolean remove(AnyType key) {
// 哈希表中存放相关key的链表
LinkedList list = theLists[hash3(key)];
if(list.contains(key)) {
list.remove(key);
currentSize--;
return true;
}
return false;
}
}
分离链接散列法的一般法则是使得表的大小和预料的元素个数大致相等,就是装填因子(元素个数/表的大小)约等于1,如果装填因子大于1,就进行扩容