哈希表的基本介绍:
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表
实际上哈希表就是模拟的数组,只不过数组中的每一个元素又是链表
如图:
因为哈希表结合了数组和链表两种数据结构,所以既利用的数组查询效率高的优点也利用了链表增删效率高的优点,虽然不能完全利用,但整体上提高了不少效率。哈希表的效率是否高,还要取决于哈希函数的创建是否合理.
哈希函数的介绍:
什么是哈希函数?在记录的关键字与记录的存储地址之间建立的一种对应关系叫哈希函数。 哈希函数就是一种映射,是从关键字到存储地址的映射。 通常,包含哈希函数的算法的算法复杂度都假设为O(1),这就是为什么在哈希表中搜索数据的时间复杂度会被认为是"平均为O(1)的复杂度".
什么是碰撞?
冲突(碰撞) 对于不同的关键字ki、kj,若ki != kj,但H(ki) = H(kj)的现象叫冲突(collision) ,即不同的输入却有相同的输出。我们应该尽量避免冲突,因为冲突不仅会使我们在查找的时候效率变慢,还甚至会被攻击者利用从而大量消耗系统资源。所以我们定义的哈希函数应该尽量避免碰撞
创建员工对象的代码:
/**
* 表示一个员工
*/
public class Employee {
public Integer id;
public String name;
public Employee next;
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
public Employee() {
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
创建哈希表中的链表:
/**
* 表示链表
*/
public class EmpLinkedList {
//头指针,指向第一个Emp
private Employee head = new Employee();
//添加员工到链表中,按照添加的顺序排序
public void add(Employee employee) {
//如果添加第一个
if (head.next == null) {
head.next = employee;
return;
}
//如果不是第一个,则需要辅助指针找到链表最后
Employee curEmp = head;
while (true) {
if (curEmp.next == null) {
break;
}
curEmp = curEmp.next;
}
//插入尾部
curEmp.next = employee;
}
//添加员工到链表中,按照id的大小排序,从小到大
public void orderByAdd(Employee employee) {
//如果添加第一个
if (head.next == null) {
head.next = employee;
return;
}
//临时指针
Employee curEmp = head;
//标志,表示是否需要从中间插入
boolean flag = false;
//如果不是第一个,则需要寻找新加的节点应该加在哪里(比较id)
while (true) {
if (curEmp.next == null) {
break;
}
//比较id确定元素的位置
if (curEmp.next.id > employee.id) {
flag = true;
break;
}
curEmp = curEmp.next;
}
if (flag) {
//插入元素
employee.next = curEmp.next;
curEmp.next = employee;
} else {
//直接插入尾部
curEmp.next = employee;
}
}
//遍历链表
public void list(int no) {
if (head.next == null) {
System.out.println("第" + (no + 1) + "条链表为空");
return;
}
System.out.println("第" + (no + 1) + "条链表信息为:");
Employee curEmp = head;
while (true) {
if (curEmp.next == null)
break;
System.out.printf("=> id=%d name=%s\t", curEmp.next.id, curEmp.next.name);
//后移
curEmp = curEmp.next;
}
System.out.println();
}
//根据id查找雇员
public Employee selectEmployeeById(int id) {
if (head.next == null) {
//System.out.println("链表为空");
return null;
}
//辅助指针
Employee curEmp = head;
while (true) {
if (curEmp.next == null) {
curEmp = null;
break;
}
if (curEmp.next.id == id) {
break;
}
curEmp = curEmp.next;
}
return curEmp.next;
}
//根据id删除员工
public boolean deleteEmployeeById(int id) {
if (head.next == null) {
//链表为空
//System.out.println("链表为空,无法删除");
return false;
}
Employee curEmp = head;
while (true) {
//到了链表尾部
if (curEmp.next == null) {
break;
}
//如果要删除的节点是头节点
if (head.next.id == id) {
System.out.println("已删除id=" + id + "的员工,信息为:" + head.next);
if (head.next != null) {
//将头节点后移
head = head.next;
} else {
//删除唯一一个元素
head.next = null;
}
break;
} else {
//删除的是中间节点或者末尾节点
if (curEmp.next.id == id) {
//找到了要删除的员工的前一个员工
System.out.println("已删除id=" + id + "的员工,信息为:" + curEmp.next);
//删除
curEmp.next = curEmp.next.next;
break;
}
}
curEmp = curEmp.next;
}
return true;
}
}
创建哈希表:
/**
* 哈希表
*/
public class HashTable {
//创建一个元素为链表的数组
private EmpLinkedList[] empLinkedLists;
private int size;
//初始化链表
public HashTable(int size) {
this.size = size;
empLinkedLists = new EmpLinkedList[size];
//赋值
for (int i = 0; i < empLinkedLists.length; i++) {
empLinkedLists[i] = new EmpLinkedList();
}
}
//添加雇员
public void add(Employee employee) {
//根据员工的id得到该员工应该加入哪条链表中
int empLinkedListNO = hashFunction(employee.id);
//将员工存入对应的链表中
empLinkedLists[empLinkedListNO].add(employee);
}
//按照id从小到大的顺序添加雇员
public void orderByAdd(Employee employee) {
//根据员工的id得到该员工应该加入哪条链表中
int empLinkedListNO = hashFunction(employee.id);
//将员工存入对应的链表中
empLinkedLists[empLinkedListNO].orderByAdd(employee);
}
//遍历所有的链表,也就是遍历hashTable
public void list() {
for (int i = 0; i < empLinkedLists.length; i++) {
empLinkedLists[i].list(i);
}
}
//根据id查找员工
public Employee queryEmployeeById(int id) {
//使用散列函数来算出应该去哪条链表中查找
int empLinkedListNO = hashFunction(id);
Employee employee = empLinkedLists[empLinkedListNO].selectEmployeeById(id);
if (employee != null) {
System.out.println("在第" + (empLinkedListNO + 1) + "条找到了id=" + id + "的员工");
} else {
System.out.println("在哈希表中没有找到该员工");
}
return employee;
}
//根据id删除员工
public void dropEmployeeById(int id) {
//使用散列函数来算出应该去哪条链表中查找
int empLinkedListNO = hashFunction(id);
boolean isOk = empLinkedLists[empLinkedListNO].deleteEmployeeById(id);
if (isOk == false){
System.out.println("没有找到要删除的节点");
}
return;
}
//编写散列函数,根据员工的id得到该员工应该加入哪条链表中
public int hashFunction(int id) {
return id % size;
}
}
测试哈希表的功能:
import java.util.Scanner;
/**
* 测试hashTable
*/
public class HashTableDemo {
public static void main(String[] args) {
//创建hash表
HashTable hashTable = new HashTable(7);
//写一个简单的菜单
String key = "";
Scanner scanner = new Scanner(System.in);
System.out.println("add: 添加员工");
System.out.println("list: 显示员工");
System.out.println("find: 查找员工");
System.out.println("delete: 删除员工");
System.out.println("exit: 退出系统");
System.out.println("请输入选项:");
while (true){
key = scanner.next();
switch (key){
case "add":
System.out.println("输入id");
int id = scanner.nextInt();
System.out.println("输入名字");
String name = scanner.next();
Employee employee = new Employee(id,name);
hashTable.orderByAdd(employee);
break;
case "find":
System.out.println("输入id");
id = scanner.nextInt();
System.out.println(hashTable.queryEmployeeById(id));
break;
case "delete":
System.out.println("输入id");
id = scanner.nextInt();
hashTable.dropEmployeeById(id);
break;
case "list":
hashTable.list();
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}