1、开放地址法(Open Addressing):
- 线性探测(Linear Probing): 当发生冲突时,顺序地查找下一个可用的槽位,直到找到空槽或者整个表被搜索一遍。这个方法的缺点是可能出现“聚簇”(cluster)现象,即连续的槽位被占用,影响性能。在线性探测中,当发生冲突时,会顺序地查找下一个可用的槽位,直到找到空槽或者整个表被搜索一遍。
class LinearProbingHashTable {
private int[] table;
private int size;
public LinearProbingHashTable(int capacity) {
table = new int[capacity];
size = 0;
}
public void insert(int key) {
int index = hash(key);
while (table[index] != 0) {
index = (index + 1) % table.length;
}
table[index] = key;
size++;
}
// Other methods: search, delete, resize, etc.
}
- 二次探测(Quadratic Probing): 类似于线性探测,但探测的步长变成了一个二次方程,以减缓聚簇的形成。二次探测类似于线性探测,但是探测的步长变成了一个二次方程,以减缓聚簇的形成。
class QuadraticProbingHashTable {
private int[] table;
private int size;
public QuadraticProbingHashTable(int capacity) {
table = new int[capacity];
size = 0;
}
public void insert(int key) {
int index = hash(key);
int i = 1;
while (table[index] != 0) {
index = (index + i * i) % table.length;
i++;
}
table[index] = key;
size++;
}
// Other methods: search, delete, resize, etc.
}
- 双重散列(Double Hashing): 使用第二个哈希函数来计算探测步长,从而避免线性探测和二次探测中可能出现的聚簇问题。使用第二个哈希函数来计算探测步长,从而避免线性探测和二次探测中可能出现的聚簇问题。
class DoubleHashingHashTable {
private int[] table;
private int size;
public DoubleHashingHashTable(int capacity) {
table = new int[capacity];
size = 0;
}
public void insert(int key) {
int index = hash(key);
int stepSize = hash2(key);
while (table[index] != 0) {
index = (index + stepSize) % table.length;
}
table[index] = key;
size++;
}
private int hash2(int key) {
// Choose a second hash function
return 5 - (key % 5); // Example second hash function
}
// Other methods: search, delete, resize, etc.
}
2、链地址法(Chaining):
- 每个哈希桶存储一个链表(或其他数据结构,如红黑树)。当发生冲突时,新的元素被插入到相应的链表中。这样,相同哈希值的元素被存储在同一个哈希桶中,而不会影响其他哈希桶。链地址法的缺点是在处理大量冲突时可能会引入额外的空间和指针开销。
a. 建立链表(Chaining):
每个哈希桶存储一个链表,当发生冲突时,新的元素被插入到相应的链表中。
import java.util.LinkedList;
class ChainingHashTable {
private LinkedList<Integer>[] table;
private int size;
public ChainingHashTable(int capacity) {
table = new LinkedList[capacity];
size = 0;
for (int i = 0; i < capacity; i++) {
table[i] = new LinkedList<>();
}
}
public void insert(int key) {
int index = hash(key);
table[index].add(key);
size++;
}
// Other methods: search, delete, resize, etc.
}
b. Coalesced Hashing:
Coalesced Hashing是一种混合了开放地址法和链地址法思想的方法,所有元素都存储在一个大数组中。
class CoalescedHashTable {
private int[] table;
private int[] next;
private int size;
public CoalescedHashTable(int capacity) {
table = new int[capacity];
next = new int[capacity];
size = 0;
for (int i = 0; i < capacity; i++) {
next[i] = -1; // Initialize all next pointers to -1
}
}
public void insert(int key) {
int index = hash(key);
if (table[index] == 0) {
table[index] = key;
} else {
int currentIndex = index;
while (next[currentIndex] != -1) {
currentIndex = next[currentIndex];
}
next[currentIndex] = size;
table[size] = key;
}
size++;
}
// Other methods: search, delete, resize, etc.
}
3、再哈希(Rehashing):
- 当哈希表的负载因子达到一定阈值时,进行再哈希操作,即重新调整哈希表的大小。这通常涉及创建一个更大的哈希表,然后将所有已有元素重新插入。这样可以减少冲突的可能性,但是会引起一定的性能开销。
import java.util.Arrays;
class RehashingHashTable {
private int[] table;
private int size;
private static final double LOAD_FACTOR_THRESHOLD = 0.7;
public RehashingHashTable(int initialCapacity) {
table = new int[initialCapacity];
size = 0;
}
public void insert(int key) {
if ((double) size / table.length > LOAD_FACTOR_THRESHOLD) {
rehash();
}
int index = hash(key);
while (table[index] != 0) {
index = (index + 1) % table.length;
}
table[index] = key;
size++;
}
private void rehash() {
int newCapacity = table.length * 2; // Double the capacity
int[] newTable = new int[newCapacity];
Arrays.fill(newTable, 0);
for (int key : table) {
if (key != 0) {
int newIndex = key % newCapacity;
while (newTable[newIndex] != 0) {
newIndex = (newIndex + 1) % newCapacity;
}
newTable[newIndex] = key;
}
}
table = newTable;
}
// Other methods: search, delete, etc.
}
4、建立公共溢出区域(Overflow Area):
- 将所有冲突的元素都放入一个公共溢出区域。这个区域通常是一个链表或其他数据结构,而哈希表的槽位只存储指向这个区域的指针。这样可以避免在哈希表主体中进行过多的线性探测,但仍需要额外的空间。
import java.util.LinkedList;
class OverflowAreaHashTable {
private LinkedList<Integer>[] table;
private LinkedList<Integer> overflowArea;
private int size;
public OverflowAreaHashTable(int capacity) {
table = new LinkedList[capacity];
overflowArea = new LinkedList<>();
size = 0;
for (int i = 0; i < capacity; i++) {
table[i] = new LinkedList<>();
}
}
public void insert(int key) {
int index = hash(key);
if (!table[index].isEmpty()) {
overflowArea.add(key);
} else {
table[index].add(key);
}
size++;
}
// Other methods: search, delete, etc.
}
5、Coalesced Hashing:
- 这是一种混合了开放地址法和链地址法思想的方法。在Coalesced Hashing中,所有元素都存储在一个大数组中,而不是在链表中。冲突时,使用某种策略将元素插入到数组中的其他位置,以避免聚簇。
class CoalescedHashTable {
private int[] table;
private int[] next;
private int size;
public CoalescedHashTable(int capacity) {
table = new int[capacity];
next = new int[capacity];
size = 0;
Arrays.fill(next, -1); // Initialize all next pointers to -1
}
public void insert(int key) {
int index = hash(key);
if (table[index] == 0) {
table[index] = key;
} else {
int currentIndex = index;
while (next[currentIndex] != -1) {
currentIndex = next[currentIndex];
}
next[currentIndex] = size;
table[size] = key;
}
size++;
}
// Other methods: search, delete, etc.
}
我的其他博客
HTTP与HTTTPS的区别-CSDN博客
什么情况下会产生StackOverflowError(栈溢出)和OutOfMemoryError(堆溢出)怎么排查-CSDN博客
谈谈我对HashMap扩容机制的理解及底层实现-CSDN博客