哈希表又称为散列表,它是由数组和链表或者数组和二叉树构成,今天先来谈谈由数组和链表构成的哈希表
哈希表结构
下图是哈希表的结构:整个哈希表就是一个数组存放许多条链表(链表数组)
哈希函数
哈希表还有一个形影不离的伙伴叫哈希函数(散列函数)F,它是用来作关键字映射的。
每一个node都含有一个唯一标识关键字id,将id映射到哈希表的哪个HashList的工作就由哈希函数来完成,即F(id)->index,index为HashList在HashTable数组里的下标。哈希函数可以自己定义映射规则。
优势
哈希表比普通的单条链表存储结构的搜索速度更快
我们都知道,链表插入效率较高而搜索效率较低,这是因为搜索一个node的时候必须从头一个接一个地搜索,不能和数组一样快速定位。而数组是插入效率低而搜索效率高,这是因为插入可能需要大量的元素移动操作。而哈希表正是融合了这两者的优点:把一条长链表分割成好几段较短的链表,放到一个数组里。查找某个node时,先用哈希函数将node的id映射到某条链表的index,再利用数组的快速索引定位到指定链表,最后向该条链表里面搜索,这样大大减少了搜索次数,提高了搜索效率。而若是想向某条链表插入node,那也是比较高效的(通过哈希函数映射得到对应index,数组索引到对应index的这条链表,再把node插入这条链表,我们都知道,链表的插入操作是很高效率的)。
由于哈希表的搜索速率很高,因此哈希表也可以被用来做缓存。
算法代码
/**
*
* @ClassName: Node
* @Description: 链表节点
* @author: fuling
* @date: 2020年8月21日 上午12:02:03
*/
public class Node{
public int id; //node id,配合散列函数使用
public String ele; //node值
public Node next;//指向下一个node的指针
public Node(int id, String ele) {
this.id = id;
this.ele = ele;
}
@Override
public String toString() {
return "{id:" + id + ", ele:" + ele + "}";
}
}
import com.wdl.search.vo.Node;
/**
*
* @ClassName: HashTable
* @Description: 哈希表
* @author: fuling
* @date: 2020年8月20日 下午11:58:50
*/
public class HashTable {
private HashList[] hashLists;//存放hashList的数组
public HashTable(int size) {
hashLists = new HashList[size];
for(int i = 0; i < size; i++) {
hashLists[i] = new HashList();
}
}
/**
*
* @Title: HashFun
* @Description: 散列函数(哈希函数),根据id值获取index
* @param: @param id
* @return: int 数组的下标index
*/
private int HashFun(int id) {
return id % hashLists.length;
}
public void add(Node node) {
int index = HashFun(node.id);
hashLists[index].add(node);
}
public Node find(int id) {
int index = HashFun(id);
return hashLists[index].find(id);
}
public boolean update(Node node) {
int index = HashFun(node.id);
return hashLists[index].update(node);
}
public boolean delete(int id) {
int index = HashFun(id);
return hashLists[index].delete(id);
}
}
/**
*
* @ClassName: HashList
* @Description: HashTable存储的元素
* @author: fuling
* @date: 2020年8月21日 上午12:00:51
*/
class HashList{
private Node head; //指向链表的”头指针“
/**
*
* @Title: getEnd
* @Description: 找到链表最后一个node
* @return: Node 最后一个node,若链表为空,则返回null
*/
private Node getEnd() {
if(head == null)return null;
Node temp = head;
while(temp.next != null)temp = temp.next;
return temp;
}
/**
*
* @Title: add
* @Description: 添加新的node到链表
* @param: @param node 要添加的node
* @return: void
*/
public void add(Node node) {
Node end = getEnd();
if(end != null) {
end.next = node;
}else {
head = node;
}
}
/**
*
* @Title: delete
* @Description: 删除包含指定id的node
* @param: @param id 指定删除的id
* @return: boolean 删除是否成功
*/
public boolean delete(int id) {
if(head == null)return false;
Node temp = head;
while(temp.next != null && temp.next.id != id)temp = temp.next;
if(temp.next.id == id) {
temp.next = temp.next.next;
return true;
}
return false;
}
/**
*
* @Title: update
* @Description: 修改id所指向的node
* @param: @param node 要修改的node信息
* @return: boolean 修改是否成功
*/
public boolean update(Node node) {
if(head == null)return false;
Node temp = head;
while(temp.next != null && temp.next.id != node.id)temp = temp.next;
if(temp.next.id == node.id) {
node.next = temp.next.next;
temp.next = node;
return true;
}
return false;
}
/**
*
* @Title: find
* @Description: 找到id所在的node
* @param: @param id 所查找node的id
* @return: Node 找到的node,若找不到则返回null
*/
public Node find(int id) {
if(head == null)return null;
Node temp = head;
while(temp != null) {
if(temp.id == id)return temp;
temp = temp.next;
}
return null;
}
}
源码:github地址