面试的时候难免遇到手撕哈希表的问题,这里进行一些总结。
设计哈希表
简易版本
如果让你手写,一般可以试着先写一个简易版本的。
【1】底层桶的数量固定,这样的缺点就是后期哈希碰撞的可能性大大增加
【2】元素的键值仅支持int类型
【3】不支持迭代器
【4】不支持动态扩容
【5】仅支持put、get和remove
class Node{
int key, value;
Node prev, next;//双向链表
Node (int key, int value) {
this.key = key;
this.value = value;
}
}
简易版的节点可以使用以上的定义方式。
private int length = 100;//数组长度,同时也是桶的数量
private Node[] data = new Node[length];//底层数组
简易版本不支持扩容,桶的固定大小定为100,而且仅使用一个空参构造器
public int get(int key) {
int index = key % length;
Node curr = data[index];
while(curr != null) {
//遍历链表节点的数组
if (curr.key == key) {
return curr.value;
}
curr = curr.next;
}
return -1;
}
计算桶的索引的时候,直接使用取模的方式。并且注意存在哈希冲突的时候,遍历链表。
public void put(int key, int value) {
int index = key % length;//模运算,计算index
Node curr = data[index];
if (curr == null) {
//没有哈希冲突,直接插入
Node node = new Node(key, value);
data[index] = node;
return;
}
while(true) {
//存在哈希冲突
if (curr.key == key) {
//执行修改操作
curr.value = value;
return;
}
if(curr.next == null) {
//拉链法解决冲突
Node node = new Node(key, value);
node.prev = curr;
curr.next = node;
return;
} else {
curr = curr.next;
}
}
}
put操作也十分简单,先计算出对应的桶,拿到首节点进行比较(因为使用的是int类型,因此直接进行字面量比较即可)。否则遍历链表,最终的结果无法是某个链表节点的值被覆盖,或是链表尾部插入一个新的节点。
public void remove(int key) {
int index = key % length;
Node curr = data[index];
if (curr != null && curr.key == key) {
//删除第一个元素,next指针移动
Node next = curr.next;
if (next != null)