1、哈希表的介绍和内存布局
先看一个实际需求,google公司的一个上机题:
有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址…),当输入该员工的id时,要求查找到该员工的 所有信息.
要求: 不使用数据库,尽量节省内存,速度越快越好=>哈希表(散列)
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
2、哈希表的图解,
通过数组+链表的方式来快速访问。(java里的hashMap)
所以什么叫哈希表?
哈希表可以用来高效率解决元素不可重复这个问题,其本质就是:数组+链表+红黑树
3、哈希表实现 案例
有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,名字,住址…),当输入该员工的id时,要求查找到该员工的所有信息。
要求
- :不使用数据库,速度越快越好 => 哈希表(散列).
- 添加时,保证按照id从低到高插入, [课后思考:如果id不是从低到高插入,但要求各条链表仍是从低到高,怎么解决?]
- 使用链表来实现哈希表,该链表不带表头[即:链表的第一个结点就存放雇员信息]
- 思路分析并画出示意图
4、案例思路图解
使用哈希表来管理雇员信息
5、代码实现
- 员工类
//员工 代表链表上的节点
class Emp {
public int id;
public String name;
public Emp next; //next 默认为null
public Emp(){}
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- EmpLinkedList
//创建EmpLinkedList 表示链表
class EmpLinkedList{
//头指针,执行第一个Emp,因那次我们这个链表的head直接指向第一个Emp
public Emp head; //默认为null
//添加员工到链表
public void addEmp(Emp emp){
if(head == null){
head = emp;
return;
}
//不能添加已有id员工
Emp findEmp = findEmp(emp.id);
if(findEmp != null){
System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
return;
}
//不是第一个员工,定义一个辅助指针
Emp curTemp = head;
while(curTemp.next != null){
curTemp = curTemp.next;//后移
}
//退出时将emp加入链表
curTemp.next = emp;
}
//按id从小到大 添加员工到链表
public void addEmpById(Emp emp){
if(head == null){
head = emp;
return;
}
//不能添加已有id员工
Emp findEmp = findEmp(emp.id);
if(findEmp != null){
System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
return;
}
//如果新加结点比头节点小
if(head.id > emp.id){
Emp temp = null;
emp.next = head;
temp = head;
head = emp;
return;
}
//定义一个辅助指针
Emp curTemp = head;
while( true){
//遍历到末尾还未找到,添加
if(curTemp.next == null &&curTemp.id < emp.id){
curTemp.next = emp;
break;
}
if(curTemp.next.id > emp.id){
emp.next = curTemp.next;
curTemp.next = emp;
break;
}
curTemp = curTemp.next;
}
}
//遍历链表
public void list(int index){
if(head == null){
System.out.println("第"+(index+1)+"链表为空。");
return;
}
Emp curTemp = head; //辅助指针
System.out.println("第"+(index+1)+"链表信息如下:");
while(curTemp != null){
System.out.println(curTemp.toString());
curTemp = curTemp.next;
}
}
//从链表删除员工
//链表删除需要找到删除结点的前一个节点,因此分为第一个节点和第2-n结点判断
public void delete( int id){
if(head == null){
System.out.println("当前链表为空。");
return;
}
//头节点直接删除
if(head.id == id){
head = head.next;
System.out.println("Id为" + id + "的员工已经被删除~~");
return;
}
Emp curTemp = head; //辅助指针
while (true){
if(curTemp.next == null ){
break;
}
if( curTemp.next.id == id){
curTemp.next = curTemp.next.next;
System.out.println("Id为" + id + "的员工已经被删除~~");
return;//删除之后直接退出,否则还会向下执行
}
curTemp =curTemp.next;
}
}
//修改员工信息
public void update( Emp emp){
if(head == null){
System.out.println("当前链表为空。");
return;
}
Emp findEmp = findEmp(emp.id);
if(findEmp == null){
System.out.println("未找到id = "+emp.id+"员工信息");
return;
}
Emp curTemp = head; //辅助指针
while (true){
if(curTemp == null ){
break;
}
if( curTemp.id == emp.id){
curTemp.name = emp.name;
return;//跟新之后直接退出,否则还会向下执行
}
curTemp =curTemp.next;
}
}
//查找员工
public Emp findEmp( int id){
if(head == null){
System.out.println("当前链表为空。");
return null;
}
Emp curTemp = head; //辅助指针
while (curTemp != null){
if( curTemp.id == id){
//System.out.println(curTemp.toString());
return curTemp;
}
curTemp =curTemp.next;
}
return null;
}
//根据id查询雇员
//如果查找到,就返回Emp,没有找到,就返回null
public Emp findEmpId(int id){
//判断链表是否为空
if(head == null){
System.out.println("链表为空.");
return null;
}
//辅助指针
Emp curEmp = head;
while(true){
if(curEmp.id == id){ //找到
break; //此时curEmp就指向要查找的雇员
}
//退出
if(curEmp.next == null){ //未找到该雇员
curEmp = null;
break;
}
curEmp = curEmp.next; //后移
}
return curEmp;
}
}
- HashTable
//创建HashTable 管理多条链表
class HashTable{
private EmpLinkedList[] LinkedList;
private int size ;//表示共有多少条链表
public HashTable(int size){
this.size = size;
//初始化LinkedList
LinkedList = new EmpLinkedList[size];
//还未初始化 null , 分别为每个链表初始化
for (int i = 0; i < size; i++) {
LinkedList[i] = new EmpLinkedList();
}
}
//添加雇员
public void add(Emp emp){
//根据员工id,找到员工应该在哪条链表下
int LinkedListIndex = hashFun(emp.id);
//将emp 添加到对应的链表中
// LinkedList[LinkedListIndex].addEmp(emp);
//按id大小进行排序
LinkedList[LinkedListIndex].addEmpById(emp);
}
//遍历哈希表
public void list(){
for (int i = 0; i < size; i++) {
LinkedList[i].list(i);
}
}
public void findEmpId(int id){
//得到该员工id在hashtable上那个角标
int index = hashFun(id);
//进入该链表查找员工
Emp emp = LinkedList[index].findEmp(id);
if(emp == null){
System.out.println("未找到");;//表示遍历完,还未找到该节点 返回null
}else{
System.out.println(emp.toString());
}
}
public void delEmpId(int id){
//得到该员工id在hashtable上那个角标
int index = hashFun(id);
//进入该链表查找员工
LinkedList[index].delete(id);
}
public void updateEmp(Emp emp){
int index = hashFun(emp.id);
//进入该链表查找员工
LinkedList[index].update(emp);
}
//编写一个散列函数,使用取模法
public int hashFun(int id){
return id % size;
}
}
- Demo测试
public class HashTableDemo {
public static void main(String[] args) {
//创建哈希表
HashTable hashTable = new HashTable(7);
//写一个简单的菜单
String key = "";
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("雇员管理系统:");
System.out.println("add : 添加雇员 find: 查找雇员");
System.out.println("list: 显示雇员 del : 删除雇员");
System.out.println("update:跟新员工 exit: 退出系统");
key = scanner.next();
switch (key) {
case "add":
System.out.print("输入id:");
int id = scanner.nextInt();
System.out.print("输入名字:");
String name = scanner.next();
//创建雇员
Emp emp = new Emp(id, name);
hashTable.add(emp);
break;
case "list":
hashTable.list();
break;
case "find":
System.out.print("请输入需要查找的id:");
id = scanner.nextInt();
hashTable.findEmpId(id);
break;
case "del":
System.out.print("请输入雇员的id:");
id = scanner.nextInt();
hashTable.delEmpId(id);
break;
case "update":
System.out.print("输入id:");
id = scanner.nextInt();
System.out.print("输入名字:");
name = scanner.next();
//创建雇员
emp = new Emp(id, name);
hashTable.updateEmp(emp);
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}
- 代码汇总
package h_HashTable;
import java.util.LinkedList;
import java.util.Scanner;
public class HashTableDemo {
public static void main(String[] args) {
//创建哈希表
HashTable hashTable = new HashTable(7);
//写一个简单的菜单
String key = "";
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("雇员管理系统:");
System.out.println("add : 添加雇员 find: 查找雇员");
System.out.println("list: 显示雇员 del : 删除雇员");
System.out.println("update:跟新员工 exit: 退出系统");
key = scanner.next();
switch (key) {
case "add":
System.out.print("输入id:");
int id = scanner.nextInt();
System.out.print("输入名字:");
String name = scanner.next();
//创建雇员
Emp emp = new Emp(id, name);
hashTable.add(emp);
break;
case "list":
hashTable.list();
break;
case "find":
System.out.print("请输入需要查找的id:");
id = scanner.nextInt();
hashTable.findEmpId(id);
break;
case "del":
System.out.print("请输入雇员的id:");
id = scanner.nextInt();
hashTable.delEmpId(id);
break;
case "update":
System.out.print("输入id:");
id = scanner.nextInt();
System.out.print("输入名字:");
name = scanner.next();
//创建雇员
emp = new Emp(id, name);
hashTable.updateEmp(emp);
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}
//员工 代表链表上的节点
class Emp {
public int id;
public String name;
public Emp next; //next 默认为null
public Emp(){}
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
//创建HashTable 管理多条链表
class HashTable{
private EmpLinkedList[] LinkedList;
private int size ;//表示共有多少条链表
public HashTable(int size){
this.size = size;
//初始化LinkedList
LinkedList = new EmpLinkedList[size];
//还未初始化 null , 分别为每个链表初始化
for (int i = 0; i < size; i++) {
LinkedList[i] = new EmpLinkedList();
}
}
//添加雇员
public void add(Emp emp){
//根据员工id,找到员工应该在哪条链表下
int LinkedListIndex = hashFun(emp.id);
//将emp 添加到对应的链表中
// LinkedList[LinkedListIndex].addEmp(emp);
//按id大小进行排序
LinkedList[LinkedListIndex].addEmpById(emp);
}
//遍历哈希表
public void list(){
for (int i = 0; i < size; i++) {
LinkedList[i].list(i);
}
}
public void findEmpId(int id){
//得到该员工id在hashtable上那个角标
int index = hashFun(id);
//进入该链表查找员工
Emp emp = LinkedList[index].findEmp(id);
if(emp == null){
System.out.println("未找到");;//表示遍历完,还未找到该节点 返回null
}else{
System.out.println(emp.toString());
}
}
public void delEmpId(int id){
//得到该员工id在hashtable上那个角标
int index = hashFun(id);
//进入该链表查找员工
LinkedList[index].delete(id);
}
public void updateEmp(Emp emp){
int index = hashFun(emp.id);
//进入该链表查找员工
LinkedList[index].update(emp);
}
//编写一个散列函数,使用取模法
public int hashFun(int id){
return id % size;
}
}
//创建EmpLinkedList 表示链表
class EmpLinkedList{
//头指针,执行第一个Emp,因那次我们这个链表的head直接指向第一个Emp
public Emp head; //默认为null
//添加员工到链表
public void addEmp(Emp emp){
if(head == null){
head = emp;
return;
}
//不能添加已有id员工
Emp findEmp = findEmp(emp.id);
if(findEmp != null){
System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
return;
}
//不是第一个员工,定义一个辅助指针
Emp curTemp = head;
while(curTemp.next != null){
curTemp = curTemp.next;//后移
}
//退出时将emp加入链表
curTemp.next = emp;
}
//按id从小到大 添加员工到链表
public void addEmpById(Emp emp){
if(head == null){
head = emp;
return;
}
//不能添加已有id员工
Emp findEmp = findEmp(emp.id);
if(findEmp != null){
System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
return;
}
//如果新加结点比头节点小
if(head.id > emp.id){
Emp temp = null;
emp.next = head;
temp = head;
head = emp;
return;
}
//定义一个辅助指针
Emp curTemp = head;
while( true){
//遍历到末尾还未找到,添加
if(curTemp.next == null &&curTemp.id < emp.id){
curTemp.next = emp;
break;
}
if(curTemp.next.id > emp.id){
emp.next = curTemp.next;
curTemp.next = emp;
break;
}
curTemp = curTemp.next;
}
}
//遍历链表
public void list(int index){
if(head == null){
System.out.println("第"+(index+1)+"链表为空。");
return;
}
Emp curTemp = head; //辅助指针
System.out.println("第"+(index+1)+"链表信息如下:");
while(curTemp != null){
System.out.println(curTemp.toString());
curTemp = curTemp.next;
}
}
//从链表删除员工
//链表删除需要找到删除结点的前一个节点,因此分为第一个节点和第2-n结点判断
public void delete( int id){
if(head == null){
System.out.println("当前链表为空。");
return;
}
//头节点直接删除
if(head.id == id){
head = head.next;
System.out.println("Id为" + id + "的员工已经被删除~~");
return;
}
Emp curTemp = head; //辅助指针
while (true){
if(curTemp.next == null ){
break;
}
if( curTemp.next.id == id){
curTemp.next = curTemp.next.next;
System.out.println("Id为" + id + "的员工已经被删除~~");
return;//删除之后直接退出,否则还会向下执行
}
curTemp =curTemp.next;
}
}
//修改员工信息
public void update( Emp emp){
if(head == null){
System.out.println("当前链表为空。");
return;
}
Emp findEmp = findEmp(emp.id);
if(findEmp == null){
System.out.println("未找到id = "+emp.id+"员工信息");
return;
}
Emp curTemp = head; //辅助指针
while (true){
if(curTemp == null ){
break;
}
if( curTemp.id == emp.id){
curTemp.name = emp.name;
return;//跟新之后直接退出,否则还会向下执行
}
curTemp =curTemp.next;
}
}
//查找员工
public Emp findEmp( int id){
if(head == null){
System.out.println("当前链表为空。");
return null;
}
Emp curTemp = head; //辅助指针
while (curTemp != null){
if( curTemp.id == id){
//System.out.println(curTemp.toString());
return curTemp;
}
curTemp =curTemp.next;
}
return null;
}
//根据id查询雇员
//如果查找到,就返回Emp,没有找到,就返回null
public Emp findEmpId(int id){
//判断链表是否为空
if(head == null){
System.out.println("链表为空.");
return null;
}
//辅助指针
Emp curEmp = head;
while(true){
if(curEmp.id == id){ //找到
break; //此时curEmp就指向要查找的雇员
}
//退出
if(curEmp.next == null){ //未找到该雇员
curEmp = null;
break;
}
curEmp = curEmp.next; //后移
}
return curEmp;
}
}