哈希表思路图解和代码实现

原文链接传送门

哈希表(散列)-Google上机题

看一个实际需求,google公司的一个上机题:

有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址…),当输入该员工的id时,要求查找到该员工的 所有信息.

要求: 不使用数据库,尽量节省内存,速度越快越好=>哈希表(散列)

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。 15 111 % 15

img

哔哩哔哩动画

img img

哔哩哔哩动画

哈希表就是数组里面存储链表

google公司的一个上机题:

有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,名字,住址…),当输入该员工的id时,要求查找到该员工的 所有信息.

要求:

不使用数据库,速度越快越好=>哈希表(散列) 添加时,保证按照id从低到高插入 [课后思考:如果id不是从低到高插入,但要求各条链表仍是从低到高,怎么解决?]

  1. 使用链表来实现哈希表, 该链表不带表头[即: 链表的第一个结点就存放雇员信息]
  2. 思路分析并画出示意图
  3. 代码实现[增删改查(显示所有员工,按id查询)]

// 哈希表之所以能够提高效率,是因为他能够同时管理多个链表

Emp

/*** 表示一个雇员*/
class Emp{public int id;public String name;public Emp next;//next 默认为空public Emp(int id, String name) {this.id = id;this.name = name;}
}

HashTab

///创建hashTab 管理多条链表class HashTab{// 数组里面放的是链表private EmpLinkedList[] empLinkedListArray;//private Integer size = 7;// 构造器public HashTab(int size) {// 初始化empLinkedListArrayempLinkedListArray = new EmpLinkedList[size];// ? 留一个坑// 这里能直接用么/** add:添加雇员list:显示雇员exit:退出雇员add输入idtomException in thread "main" java.util.InputMismatchExceptionat java.util.Scanner.throwFor(Scanner.java:864)at java.util.Scanner.next(Scanner.java:1485)at java.util.Scanner.nextInt(Scanner.java:2117)at java.util.Scanner.nextInt(Scanner.java:2076)at com.atguigu.hashtab.HashTabDemo.main(HashTabDemo.java:30)Process finished with exit code 1* */// 这个时候不要忘了, 分别初始化 每个链表for (int i = 0; i < size; i++) {// 数组中的每一个元素都需要创建一把empLinkedListArray[i] = new EmpLinkedList();}}// 添加雇员public void add(Emp emp) {// 根据员工的id,得到该员工应当加入到,哪条链表int empLinkedListNO = hashFun(emp.id);// 将emp 添加到对应的链表中empLinkedListArray[empLinkedListNO].add(emp);}// 不管你是什么操作,总是要找链表// 遍历所有的链表public void list() {// 遍历Hash表for (int i = 0; i < size; i++) {empLinkedListArray[i].list(i);}}// 编写一个散列函数// 散列函数有很多种写法// 这里使用简单的方法-取模法public int hashFun(int id) {return id % size;}/*** 更根据输入的id,查找雇员* @param id*/public void findEmpById(int id) {// 使用散列函数确定到哪条链表查找int empLinkedListNO = hashFun(id);Emp emp = empLinkedListArray[empLinkedListNO].findEmpById(id);if (emp != null) {// 说明找到了System.out.println("找到了该雇员");System.out.printf("在第%d条链表中找到了该雇员,id = %d",id,empLinkedListNO+1);} else {System.out.println("在哈希表中,没有找到该雇员~");}}
}

EmpLinkedList

class EmpLinkedList{// 头指针, 执行第一个Emp,因此我们这个链表的head,是直接 指向第一个Empprivate Emp head;//添加雇员到链表// 说明.// 1. 假定,当添加雇员的时候,id是自增长的,即id 的分配总是从小到大public void add(Emp emp) {// 如果是添加第一个雇员if (head == null) {head = emp;return;}// 如果不是添加第一个雇员,则使用一个辅助的指针,帮助定位到最后Emp currEmp = head;while (true) {if (currEmp.next == null) {// 说明到链表最后break;}// 后移currEmp = currEmp.next;}// 退出时,直接将emp 加入链表currEmp.next = emp;}// 遍历链表的雇员信息public void list(int no) {// 判断是否为空if (head == null) {// 说明链表为空System.out.println("当前链"+no+"表为空!");return;}// 没有空
//        打印信息System.out.println("当前链"+no+"表的信息为");// 辅助指针Emp currEmp = head;while (true) {System.out.printf("=> id =%d name = %s\t",currEmp.id,currEmp.name);if (currEmp.next == null) {// 说明,currEmp 已经是最后节点break;}// 后移 遍历currEmp = currEmp.next;}System.out.println();}/***     // 根据id 查找雇员*    // 如果查找到 ,就返回Emp,如果没有找打到,就返回null* @param id* @return*/public Emp findEmpById(int id) {// 判断链表是否为空if (head == null) {System.out.println("链表为空");return null;}//辅助指针Emp curEmp = head;while (true) {//if (curEmp.id == id) {// 找到break;//  这个时候,currEmp就指向了要查找的雇员}// 退出if (curEmp.next == null) {// 说明遍历当前链表没有找到该雇员curEmp = null;}// 后移curEmp = curEmp.next;}return curEmp;}
}

主函数

public static void main(String[] args) {// 创建哈希表HashTab hashTab = new HashTab(7);// 写一个简单的菜单来测试String key = "";Scanner scanner = new Scanner(System.in);while (true) {System.out.println("add:添加雇员");System.out.println("list:显示雇员");System.out.println("find:查找雇员");System.out.println("exit:退出雇员");key = scanner.next();switch (key) {case "add":System.out.println("输入id");int id = scanner.nextInt();System.out.println("输入名字");String name = scanner.next();// 创建雇员Emp emp = new Emp(id, name);hashTab.add(emp);break;case "list":hashTab.list();break;case "find":System.out.println("输入id");int findId = scanner.nextInt();hashTab.findEmpById(findId);break;case "exit":scanner.close();System.exit(0);default:break;}}
}
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
1
输入名字
tom
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
2
输入名字
jack
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
3
输入名字
pin
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
6
输入名字
nanc
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
list
当前链0表为空!
当前链1表的信息为
=> id =1 name = tom	
当前链2表的信息为
=> id =2 name = jack	
当前链3表的信息为
=> id =3 name = pin	
当前链4表为空!
当前链5表为空!
当前链6表的信息为
=> id =6 name = nanc	
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
123
输入名字
sme
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
list
当前链0表为空!
当前链1表的信息为
=> id =1 name = tom	
当前链2表的信息为
=> id =2 name = jack	
当前链3表的信息为
=> id =3 name = pin	
当前链4表的信息为
=> id =123 name = sme	
当前链5表为空!
当前链6表的信息为
=> id =6 name = nanc	
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
678
输入名字
vicr
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
list
当前链0表为空!
当前链1表的信息为
=> id =1 name = tom	
当前链2表的信息为
=> id =2 name = jack	
当前链3表的信息为
=> id =3 name = pin	
当前链4表的信息为
=> id =123 name = sme	
当前链5表为空!
当前链6表的信息为
=> id =6 name = nanc	=> id =678 name = vicr	
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
389
输入名字
wef
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
9
输入名字
zho
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
list
当前链0表为空!
当前链1表的信息为
=> id =1 name = tom	
当前链2表的信息为
=> id =2 name = jack	=> id =9 name = zho	
当前链3表的信息为
=> id =3 name = pin	
当前链4表的信息为
=> id =123 name = sme	=> id =389 name = wef	
当前链5表为空!
当前链6表的信息为
=> id =6 name = nanc	=> id =678 name = vicr	
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
34
输入名字
mach
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
list
当前链0表为空!
当前链1表的信息为
=> id =1 name = tom	
当前链2表的信息为
=> id =2 name = jack	=> id =9 name = zho	
当前链3表的信息为
=> id =3 name = pin	
当前链4表的信息为
=> id =123 name = sme	=> id =389 name = wef	
当前链5表为空!
当前链6表的信息为
=> id =6 name = nanc	=> id =678 name = vicr	=> id =34 name = mach	
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
find
输入id
8
Exception in thread "main" java.lang.NullPointerExceptionat com.atguigu.hashtab.EmpLinkedList.findEmpById(HashTabDemo.java:237)at com.atguigu.hashtab.HashTab.findEmpById(HashTabDemo.java:128)at com.atguigu.hashtab.HashTabDemo.main(HashTabDemo.java:44)Process finished with exit code 1

最后这里置空要 加上break

if (curEmp.next == null) {// 说明遍历当前链表没有找到该雇员curEmp = null;break;}

这下就可以了

add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
1
输入名字
tom
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
2
输入名字
nancy
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add4
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
add
输入id
4
输入名字
victor
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
list
当前链0表为空!
当前链1表的信息为
=> id =1 name = tom	
当前链2表的信息为
=> id =2 name = nancy	
当前链3表为空!
当前链4表的信息为
=> id =4 name = victor	
当前链5表为空!
当前链6表为空!
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
find
输入id
6
链表为空
在哈希表中,没有找到该雇员~
add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员
find
输入id
4
找到了该雇员
在第4条链表中找到了该雇员,id = 5add:添加雇员
list:显示雇员
find:查找雇员
exit:退出雇员

完整代码

package com.atguigu.hashtab;import java.util.Scanner;/*** ClassName:  <br/>* Description:  <br/>* Date: 2021-02-24 13:10 <br/>* <br/>* @project data_algorithm* @package com.atguigu.hashtab*/
public class HashTabDemo {public static void main(String[] args) {// 创建哈希表HashTab hashTab = new HashTab(7);// 写一个简单的菜单来测试String key = "";Scanner scanner = new Scanner(System.in);while (true) {System.out.println("add:添加雇员");System.out.println("list:显示雇员");System.out.println("find:查找雇员");System.out.println("exit:退出雇员");key = scanner.next();switch (key) {case "add":System.out.println("输入id");int id = scanner.nextInt();System.out.println("输入名字");String name = scanner.next();// 创建雇员Emp emp = new Emp(id, name);hashTab.add(emp);break;case "list":hashTab.list();break;case "find":System.out.println("输入id");int findId = scanner.nextInt();hashTab.findEmpById(findId);break;case "exit":scanner.close();System.exit(0);default:break;}}}
}///创建hashTab 管理多条链表class HashTab{// 数组里面放的是链表private EmpLinkedList[] empLinkedListArray;//private Integer size = 7;// 构造器public HashTab(int size) {// 初始化empLinkedListArrayempLinkedListArray = new EmpLinkedList[size];// ? 留一个坑// 这里能直接用么/** add:添加雇员list:显示雇员exit:退出雇员add输入idtomException in thread "main" java.util.InputMismatchExceptionat java.util.Scanner.throwFor(Scanner.java:864)at java.util.Scanner.next(Scanner.java:1485)at java.util.Scanner.nextInt(Scanner.java:2117)at java.util.Scanner.nextInt(Scanner.java:2076)at com.atguigu.hashtab.HashTabDemo.main(HashTabDemo.java:30)Process finished with exit code 1* */// 这个时候不要忘了, 分别初始化 每个链表for (int i = 0; i < size; i++) {// 数组中的每一个元素都需要创建一把empLinkedListArray[i] = new EmpLinkedList();}}// 添加雇员public void add(Emp emp) {// 根据员工的id,得到该员工应当加入到,哪条链表int empLinkedListNO = hashFun(emp.id);// 将emp 添加到对应的链表中empLinkedListArray[empLinkedListNO].add(emp);}// 不管你是什么操作,总是要找链表// 遍历所有的链表public void list() {// 遍历Hash表for (int i = 0; i < size; i++) {empLinkedListArray[i].list(i);}}// 编写一个散列函数// 散列函数有很多种写法// 这里使用简单的方法-取模法public int hashFun(int id) {return id % size;}/*** 更根据输入的id,查找雇员* @param id*/public void findEmpById(int id) {// 使用散列函数确定到哪条链表查找int empLinkedListNO = hashFun(id);Emp emp = empLinkedListArray[empLinkedListNO].findEmpById(id);if (emp != null) {// 说明找到了System.out.println("找到了该雇员");System.out.printf("在第%d条链表中找到了该雇员,id = %d",id,empLinkedListNO+1);} else {System.out.println("在哈希表中,没有找到该雇员~");}}
}/*** 表示一个雇员*/
class Emp{public int id;public String name;public Emp next;//next 默认为空public Emp(int id, String name) {this.id = id;this.name = name;}
}class EmpLinkedList{// 头指针, 执行第一个Emp,因此我们这个链表的head,是直接 指向第一个Empprivate Emp head;//添加雇员到链表// 说明.// 1. 假定,当添加雇员的时候,id是自增长的,即id 的分配总是从小到大public void add(Emp emp) {// 如果是添加第一个雇员if (head == null) {head = emp;return;}// 如果不是添加第一个雇员,则使用一个辅助的指针,帮助定位到最后Emp currEmp = head;while (true) {if (currEmp.next == null) {// 说明到链表最后break;}// 后移currEmp = currEmp.next;}// 退出时,直接将emp 加入链表currEmp.next = emp;}// 遍历链表的雇员信息public void list(int no) {// 判断是否为空if (head == null) {// 说明链表为空System.out.println("当前链"+no+"表为空!");return;}// 没有空
//        打印信息System.out.println("当前链"+no+"表的信息为");// 辅助指针Emp currEmp = head;while (true) {System.out.printf("=> id =%d name = %s\t",currEmp.id,currEmp.name);if (currEmp.next == null) {// 说明,currEmp 已经是最后节点break;}// 后移 遍历currEmp = currEmp.next;}System.out.println();}/***     // 根据id 查找雇员*    // 如果查找到 ,就返回Emp,如果没有找打到,就返回null* @param id* @return*/public Emp findEmpById(int id) {// 判断链表是否为空if (head == null) {System.out.println("链表为空");return null;}//辅助指针Emp curEmp = head;while (true) {//if (curEmp.id == id) {// 找到break;//  这个时候,currEmp就指向了要查找的雇员}// 退出if (curEmp.next == null) {// 说明遍历当前链表没有找到该雇员curEmp = null;break;}// 后移curEmp = curEmp.next;}return curEmp;}
}

扩展

删除功能???

原文链接传送门

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/455065.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

glibc交叉编译_TSN之linuxptp交叉编译

0 开发环境1 linuxptp是什么2 为什么要交叉编译linuxptp3 修改makefile4 修改源码5 交叉编译0 开发环境笔记本&#xff1a;ubuntu18.04.5&#xff0c;内核版本为5.3 开发板&#xff1a;imx8mp-evk内核版本&#xff1a;Linux5.4.24交叉编译工具链&#xff1a;fsl-imx-xwayland-g…

声音编码

1.脉冲编码调制PCM文件格式简介 将音频数字化&#xff0c;其实就是将声音数字化。最常见的方式是透过脉冲编码调制PCM(Pulse Code Modulation) 。运作原理如下。首先我们考虑声音经过麦克风&#xff0c;转换成一连串电压变化的信号&#xff0c;如图一所示。这张图的横座标为秒&…

Elastic Stack简介

Elastic Stack简介 如果你没有听说过Elastic Stack&#xff0c;那你一定听说过ELK&#xff0c;实际上ELK是三款软件的简称&#xff0c;分别是Elasticsearch、 Logstash、Kibana组成&#xff0c;在发展的过程中&#xff0c;又有新成员Beats的加入&#xff0c;所以就形成了Elast…

webpack v3 结合 react-router v4 做 dynamic import — 按需加载(懒加载)

为什么要做dynamic import&#xff1f; dynamic import不知道为什么有很多叫法&#xff0c;什么按需加载&#xff0c;懒加载&#xff0c;Code Splitting&#xff0c;代码分页等。总之&#xff0c;就是在SPA&#xff0c;把JS代码分成N个页面份数的文件&#xff0c;不在用户刚进来…

go kegg_工具篇丨GO和KEGG富集不到通路?快试试这个超赞的功能分析工具吧

GO和KEGG富集分析是我们在筛选出差异表达基因之后&#xff0c;都会去做的套路性分析。然鹅……我相信&#xff0c;总有那么一些“倒霉孩子”会遇到跟我一样的窘境吧&#xff0c;好不容易筛选出来的差异基因&#xff0c;尝试了DAVID(https://david.ncifcrf.gov/)、Metascape(htt…

搭建Telnet服务器

搭建Telnet服务器 作者&#xff1a;尹正杰 版权声明&#xff1a;原创作品&#xff0c;谢绝转载&#xff01;否则将追究法律责任。 可能大家都知道现在已经很少有人用TELNET服务器&#xff0c; 因为它传输数据是以明文的方式&#xff0c;我们很容易通过抓包软件讲数据进行抓包&a…

table取tr对象 vue_Vue笔记

Vue集成了React和Angular的优点&#xff0c;摒弃了他们的缺点。Vue的官网&#xff1a;https://cn.vuejs.org/v2/api/Vue诞生于2016年&#xff0c;是现在非常流行的MVVM框架。Vue提供了“引包”的使用方法&#xff0c;初学者可以在这之下学习语法。不需要webpack&#xff0c;不需…

Beats入门简介

使用Beat收集nginx日志和指标数据 项目需求 Nginx是一款非常优秀的web服务器&#xff0c;往往nginx服务会作为项目的访问入口&#xff0c;那么&#xff0c;nginx的性能保障就变得非常重要了&#xff0c;如果nginx的运行出现了问题就会对项目有较大的影响&#xff0c;所以&…

可编程led灯带原理_SCPSD-250-04-27派克真空压力传感器故障和工作原理

SCPSD-250-04-27派克PARKER真空压力传感器故障和工作原理PARKER压力开关现货 PARKER压力传感器特价 派克真空压力传感器 PARKER数字压力开关2020年还剩最后2天了&#xff0c;这一年大家都过得不太容易&#xff0c;尤其是我自己这是30年以来过得最艰难的一年&#xff0c;经…

Kibana入门安装与介绍

Kibana入门 Kibana 是一款开源的数据分析和可视化平台&#xff0c;它是 Elastic Stack 成员之一&#xff0c;设计用于和 Elasticsearch 协作。您可以使用 Kibana 对 Elasticsearch 索引中的数据进行搜索、查看、交互操作。您可以很方便的利用图表、表格及地图对数据进行多元化…

友善串口工具接收数据随机换行_使用Python3+PyQT5+Pyserial 实现简单的串口工具方法...

练手项目&#xff0c;先上图先实现一个简单的串口工具&#xff0c;为之后的上位机做准备代码如下&#xff1a;pyserial_demo.pyimport sys import serial import serial.tools.list_ports from PyQt5 import QtWidgets from PyQt5.QtWidgets import QMessageBox from PyQt5.QtC…

Vue渲染函数

前面的话 Vue 推荐在绝大多数情况下使用 template 来创建HTML。然而在一些场景中&#xff0c;真的需要 JavaScript 的完全编程的能力&#xff0c;这就是 render 函数&#xff0c;它比 template 更接近编译器。本文将详细介绍Vue渲染函数 引入 下面是一个例子&#xff0c;如果要…

Logstash入门简介

Logstash入门简介 介绍 Logstash是一个开源的服务器端数据处理管道&#xff0c;能够同时从多个来源采集数据&#xff0c;转换数据&#xff0c;然后将数据发送到最喜欢的存储库中&#xff08;我们的存储库当然是ElasticSearch&#xff09; 我们回到我们ElasticStack的架构图&a…

Django templates 和 urls 拆分

如果在Django项目 下面新建了blog和polls两个APP应用&#xff0c;在每个APP下面都各自新建自己的url和templates&#xff0c;那么我们需要如何进行项目配置呢&#xff1f; INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, dja…

springboot怎么杀进程_线上服务平均响应时间太长,怎么排查?

线上服务平均响应时间太长&#xff0c;怎么排查&#xff1f;https://xie.infoq.cn/article/914b5c56000a3880016abd8d6前言&#xff1a;最近线上环境某个接口服务响应时间偏长&#xff0c;导致用户体验超差&#xff0c;那平时该怎么快速的排查这类问题呢&#xff1f;①、为代码…

MPEG音视频编解码之MP3编解码概述

2 MP3编解码原理 2.1 MP3音频压缩标准概述 MP3全称是动态影像专家压缩标准音频层面3&#xff08;Moving Picture Experts Group Audio Layer III&#xff09;。是当今较流行的一种数字音频编码和有损压缩格式&#xff0c;它设计用来大幅度地降低音频数据量&#xff0c;而对于…

Python实现GitBook工具

写在前面 本工具是通过Python脚本实现 GitBook 自动 生成 执行 编译 发布的功能 你可以在这里下载exe 使用 1. exe下载,并移动位置 将exe文件放在你的gitbook文件夹中,或者放在空文件夹中 2. file.md 创建 名为file.md的文件,在你要写book的目录下 注意: 这里file.md文件名…

shell脚本中用到的条件和循环语句

本博文介绍一下shell脚本中常用的条件和循环语句&#xff1a;条件语句&#xff1a;循环语句&#xff1a;示例&#xff1a;if语句&#xff1a;eg1.eg2.2.case语句&#xff1a;简单的case语句&#xff1a;配合循环的case语句&#xff1a;3.for语句&#xff1a;简单的for语句&…

processing动态代码大全_做一张动态海报需要多少步?

人们习惯性地把程序员跟设计师分成两种不同性质的人&#xff0c;好像程序员就不会有美感&#xff0c;设计师逻辑思维就一定会很弱&#xff0c;但最近几年我们发现越来越多的程序员学设计&#xff0c;设计师学编程的跨界故事。新媒体艺术家&#xff0c;邱伟豪也是其中一员&#…

手写springboot_Spring Boot 入门教程 | 图文讲解

目录一、Spring Boot 是什么二、为什么要使用 Spring Boot三、快速入门3.1 创建 Spring Boot 项目3.2 项目结构3.3 引入 Web 依赖3.4 编写第一个接口3.5 启动程序&#xff0c;验证效果四、总结五、GitHub 示例代码一、Spring Boot 是什么以下截图自 Spring Boot 官方文档&#…