哈希表
看一个实际需求,google公司的一个上机题:
有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址…),当输入该员工的id时,要求查找到该员工的 所有信息.
要求: 不使用数据库,尽量节省内存,速度越快越好=>哈希表(散列)
- 散列表(Hashtable,也叫哈希表),是根据关键码值(Keyvalue)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
-
上述图例为韩顺平老师所制。
-
题目分析:题目要求其实就是实现对象的增删改查,无论你是用queue还是链表都是可以实现的,但是此处要求就是越快越好,面试的情况下这种题目说最简单的当然不是最优解,那么哈希表(散列表)就能在链表的基础上更加提升性能,减少了遍历的时间,数据量越大分的链表越多那么相对减少的时间是十分可观的。
-
实现:一个数组中放多个链表,放入哪个由id的hash得到,此处用取模得到位置。
-
过程:员工对象 -> 链表 -> hash表 -> 实现
-
链表类
class LinkedListEmp{
private Emp head;//头结点,每个链表需要头结点来定位
//添加员工
public void add(Emp nemp){
if (head == null){//为空直接添加
head = nemp;
return;
}
Emp temp = head;//一个临时变量
while (true){
if (temp.next==null){//成立时说明遍历到链表最后了
break;
}
temp = temp.next;//没到最后就一直向后遍历
}
temp.next = nemp;//新员工添加到最后
}
//展示所有的员工
public void show(int n){
if (head == null){
System.out.println("链表"+(n+1)+"为空;");
return;
}
Emp temp = head;
System.out.print("链表"+(n+1)+":");
while (true){
System.out.printf("->> %d :%s\t" , temp.id , temp.name);
if (temp.next == null){
break;
}
temp=temp.next;
}
System.out.println();
}
//根据id找员工
public Emp findEmpByID(int id){
if (head == null){
System.out.println("此链表为空!");
}
Emp temp = head;
while (true){
if (temp.id == id){//找到员工号就退出
break;
}
temp = temp.next;
if (temp.next==null){
return null;//没找到返回null
}
}
return temp;//返回这个员工
}
public void deleteEmpById(int id){//删除员工
if (head == null){
System.out.println("此链表为空!");
}
Emp temp = head;
while (true){//此处与普通单链表有区别,如果头结点就是要找的员工,那么直接换头结点
if (head.id == id){
head = temp.next;
System.out.println("删除员工:"+id);
break;
}else if (temp.next.id == id){//如果找到将next域指向下下一个对象,那么中间的对象就抛弃了,也就删除了
temp.next = temp.next.next;
System.out.println("删除员工:"+id);
break;
}
if (temp.next == null){
System.out.println("没有找到此员工!!");
break;
}
temp = temp.next;
}
}
}
- 员工类
//员工类
class Emp{
int id;
String name ;
Emp next = null;
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
}
- 哈希表
//哈希表
class HashEmpTable{
int size;
LinkedListEmp[] hashEmpTable;//定义一个以链表为数据类型的数组
public HashEmpTable(int size) {//初始化数组和确认空间大小
this.size=size;
hashEmpTable = new LinkedListEmp[size];
for (int i = 0; i < size; i++) {
hashEmpTable[i] = new LinkedListEmp();
}
}
//添加
public void add(Emp emp){//直接利用方法取到这个id号的员工所在的链表,用那个链表调用。
int funId = hashFunId(emp.id);
hashEmpTable[funId].add(emp);
}
//展示
public void show(){
for (int i = 0; i < size; i++) {
hashEmpTable[i].show(i);
}
}
//删除
public void deleteEmpById(int id){//同样,找到链表再删除对象
int funId = hashFunId(id);
hashEmpTable[funId].deleteEmpById(id);
}
//find员工,
public void findEmpById(int id){
int funId = hashFunId(id);
Emp emp = hashEmpTable[funId].findEmpByID(id);
if (emp != null){
System.out.printf("在第 %d 条链表找到员工-> %d,姓名是-> %s" , funId , emp.id , emp.name);
}
}
//利用取模实现哈希寻找链表号
public int hashFunId(int id){
int hashId = id % size;
return hashId;
}
}
//员工类
class Emp{
int id;
String name ;
Emp next = null;
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
}
- 实现类
public class HashExercise {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
HashEmpTable hashEmpTable = new HashEmpTable(7);
String oper = "";
while (true){
System.out.println("add");
System.out.println("show");
System.out.println("find");
System.out.println("delete");
System.out.println("exit");
oper = sc.next();
switch (oper){
case "add":
System.out.println("请输入员工id");
int id = sc.nextInt();
System.out.println("请输入员工姓名");
String name = sc.next();
Emp emp = new Emp(id, name);
hashEmpTable.add(emp);
break;
case "show":
hashEmpTable.show();
break;
case "find":
System.out.println("请输入需要查找的员工ID");
int findId = sc.nextInt();
hashEmpTable.findEmpById(findId);
break;
case "delete":
System.out.println("请输入需要删除的员工ID");
int deleteID = sc.nextInt();
hashEmpTable.deleteEmpById(deleteID);
break;
case "exit":
return;
}
}
}
}
将此例结合会建立对hash表的一个很好的入门思想。