哈希表中解决一个单元多个数据项的冲突问题,有2种办法:开放地址法和链地址法。开放地址法是在数据项具有相同下标时在数组中其他地方寻找一个空白单元放置要插入的数据项;链地址法是数组存储的是链表,相同下标的数据项只需要连到链表上去即可。
哈希表的容量一般设置为素数,因为无论按照何种步长,总可以遍历素数。
其中,开放地址法有3种方法:
1,线性探测:线性探测是逐步搜索空的位置。缺点:数据项聚集,越来越大,越后面找到空位插入数据项需要时间越多
import java.util.Random;
//假设数据都是正整数
class DataItem {
int data;
public DataItem(int data) {
this.data = data;
}
}
public class LHashFind {
// 数据量最好在一半到2/3之间
private int size;
private DataItem[] itemArray;
private DataItem deletedItem;
public LHashFind(int size) {
this.size = size;
itemArray = new DataItem[size];
deletedItem = new DataItem(-1);
}
// 以下方法只在数组不满时使用,否则可能死循环
public DataItem find(int key) {
int hashval = hashfunc(key);
while (itemArray[hashval] != null) {
if (itemArray[hashval].data == key)
return itemArray[hashval];
hashval++;
hashval %= size;
}
return null;
}
private int hashfunc(int key) {
return key % size;
}
public void insert(int key) {
DataItem item = new DataItem(key);
int hashval = hashfunc(key);
while (itemArray[hashval] != null && itemArray[hashval].data != -1) {
hashval++;
hashval %= size;
}
itemArray[hashval] = item;
}
public DataItem delete(DataItem item) {
int hashval = hashfunc(item.data);
while (itemArray[hashval] != null) {
if (itemArray[hashval].data == item.data) {
DataItem temp = itemArray[hashval];
itemArray[hashval] = deletedItem;
return temp;
}
hashval++;
hashval %= size;
}
return null;
}
public void display() {
for (int i = 0; i < size; i++) {
if (itemArray[i] != null)
System.out.print(itemArray[i].data + " ");
else
System.out.print(0 + " ");
}
System.out.println();
}
// 哈希表扩容:不是简单复制,而是重新插入到新的数组
public void reHash() {
int newSize = reSize(size);// 得到一个大于2倍size的素数newSize
DataItem[] newArray = new DataItem[newSize];
int key, hashval;
for (int i = 0; i < size; i++) {
if (itemArray[i] != null && itemArray[i].data != -1) {
key = itemArray[i].data;
hashval = key % newSize;
while (newArray[hashval] != null) {
hashval++;
hashval %= newSize;
}
newArray[hashval] = itemArray[i];
}
}
// 赋值
itemArray = newArray;
size = newSize;
}
private int reSize(int size) {
int nsize = 2 * size + 1;
while (true) {
if (isPrime(nsize))
return nsize;
else
nsize++;
}
}
private boolean isPrime(int nsize) {
for (int j = 2; j * j <= nsize; j++)
if (nsize % j == 0)
return false;
return true;
}
public static void main(String[] args) {
int size = 23;
LHashFind hash = new LHashFind(size);
Random rand = new Random();
for (int i = 0; i < 10; i++) {
hash.insert(rand.nextInt(1000) + 1);
}
hash.display();
hash.reHash();
hash.display();
}
}
2,二次探测:寻找和插入都是在起始下标的1,4,9,16……位置后寻找数据或将数据插入空白单元,即步长是按平方增大的,有可能超过整型范围。克服了方法1的大片聚集,但是仍然会产生二次聚集,这个问题不大。
3,再哈希法:通过给不同关键字设置不同步长解决数据项聚集的问题,开放地址法中常使用此方法。
import java.util.Random;
class DataItem {
int data;
public DataItem(int data) {
this.data = data;
}
}
public class ReHash {
private DataItem[] itemArray;
private int size;
private DataItem nonItem;
public ReHash(int size) {
this.size = size;
itemArray = new DataItem[size];
nonItem = new DataItem(-1);
}
private int hashfunc1(int key) {
return key % size;
}
// 对于不同的key,采用不同的步长,这里的5可以换成其他素数
private int hashfunc2(int key) {
return 5 - key % 5;
}
public void insert(int key) {
int hashval = hashfunc1(key);
int stepsize = hashfunc2(key);
while (itemArray[hashval] != null && itemArray[hashval].data != -1) {
hashval += stepsize;
hashval %= size;
}
itemArray[hashval] = new DataItem(key);
}
public DataItem find(int key) {
int hashval = hashfunc1(key);
int stepsize = hashfunc2(key);
while (itemArray[hashval] != null) {
if (itemArray[hashval].data == key)
return itemArray[hashval];
hashval += stepsize;
hashval %= size;
}
return null;
}
public DataItem delete(int key) {
int hashval = hashfunc1(key);
int stepsize = hashfunc2(key);
while (itemArray[hashval] != null) {
if (itemArray[hashval].data == key) {
DataItem temp = itemArray[hashval];
itemArray[hashval] = nonItem;
return temp;
}
hashval += stepsize;
hashval %= size;
}
return null;
}
public void display() {
for (int i = 0; i < size; i++)
if (itemArray[i] != null)
System.out.print(itemArray[i].data + " ");
else
System.out.print("* ");
System.out.println();
}
public static void main(String[] args) {
Random rand = new Random();
int size = 23, n = 20;
int[] array = new int[n];
ReHash hash = new ReHash(size);
for (int i = 0; i < n; i++)
array[i] = rand.nextInt(1000) + 1;
for (int key : array)
hash.insert(key);
hash.display();
System.out.println(hash.find(array[5]).data);
hash.delete(array[5]);
hash.display();
}
}