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

1) 看一个实际需求,google 公司的一个上机题:
2) 有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址..),当输入该员工的id 时,要求查
找到该员工的所有信息.
3) 要求: 不使用数据库,尽量节省内存,速度越快越好=>哈希表(散列)

9.2 哈希表的基本介绍

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

9.3 google 公司的一个上机题:

有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,名字,住址..),当输入该员工的id 时,
要求查找到该员工的所有信息.
要求:
1) 不使用数据库,,速度越快越好=>哈希表(散列)
2) 添加时,保证按照id 从低到高插入[课后思考:如果id 不是从低到高插入,但要求各条链表仍是从低到
高,怎么解决?]
3) 使用链表来实现哈希表, 该链表不带表头[即: 链表的第一个结点就存放雇员信息]

4)思路分析并画出示意图

image.png

5)代码实现

  1. package hashtab;
  2. import java.util.Scanner;
  3. public class HashTabDemo {
  4. public static void main(String[] args) {
  5. //创建哈希表
  6. HashTab hashTab = new HashTab(7);
  7. //写一个简单的菜单
  8. String key = "";
  9. Scanner scanner = new Scanner(System.in);
  10. while (true) {
  11. System.out.println("add: 添加雇员");
  12. System.out.println("list: 显示雇员");
  13. System.out.println("find: 查找雇员");
  14. System.out.println("dele: 查找雇员");
  15. System.out.println("exit: 退出系统");
  16. key = scanner.next();
  17. switch (key) {
  18. case "add":
  19. System.out.println("输入id");
  20. int id = scanner.nextInt();
  21. System.out.println("输入名字");
  22. String name = scanner.next();
  23. //创建雇员
  24. Emp emp = new Emp(id, name);
  25. hashTab.add(emp);
  26. break;
  27. case "list":
  28. hashTab.list();
  29. break;
  30. case "find":
  31. System.out.println("请输入要查找的id");
  32. id = scanner.nextInt();
  33. hashTab.findEmpById(id);
  34. break;
  35. // case"dele":
  36. // System.out.println("请输入要删除的id");
  37. // id = scanner.nextInt();
  38. // hashTab.delEmpById(id);
  39. // break;
  40. case "exit":
  41. scanner.close();
  42. System.exit(0);
  43. default:
  44. break;
  45. }
  46. }
  47. }
  48. }
  49. //创建HashTab 管理多条链表
  50. class HashTab {
  51. private EmpLinkedList[] empLinkedListArray;
  52. private int size; //表示有多少条链表
  53. //构造器
  54. public HashTab(int size) {
  55. this.size = size;
  56. //初始化empLinkedListArray
  57. empLinkedListArray = new EmpLinkedList[size];
  58. //?留一个坑, 这时不要分别初始化每个链表
  59. for (int i = 0; i < size; i++) {
  60. empLinkedListArray[i] = new EmpLinkedList();
  61. }
  62. }
  63. //添加雇员
  64. public void add(Emp emp) {
  65. //根据员工的id ,得到该员工应当添加到哪条链表
  66. int empLinkedListNO = hashFun(emp.id);
  67. //将emp 添加到对应的链表中
  68. empLinkedListArray[empLinkedListNO].add(emp);
  69. }
  70. //遍历所有的链表,遍历hashtab
  71. public void list() {
  72. for (int i = 0; i < size; i++) {
  73. empLinkedListArray[i].list(i);
  74. }
  75. }
  76. //根据输入的id,查找雇员
  77. public void findEmpById(int id) {
  78. //使用散列函数确定到哪条链表查找
  79. int empLinkedListNO = hashFun(id);
  80. Emp emp = empLinkedListArray[empLinkedListNO].findEmpById(id);
  81. if (emp != null) {//找到
  82. System.out.printf("在第%d 条链表中找到雇员id = %d\n", (empLinkedListNO + 1), id);
  83. } else {
  84. System.out.println("在哈希表中,没有找到该雇员~");
  85. }
  86. }
  87. // //根据输入的id,删除雇员
  88. // public void delEmpById(int id){
  89. // //使用散列函数确定到哪条链表查找
  90. // int empLinkedListNO = hashFun(id);
  91. // Emp emp =empLinkedListArray[empLinkedListNO].delEmpByID(id);
  92. // if (emp != null) {//找到
  93. // System.out.println("删除后哈希表为");
  94. // empLinkedListArray[empLinkedListNO].list();
  95. // } else {
  96. // System.out.println("在哈希表中,没有找到该雇员~");
  97. // }
  98. // }
  99. //编写散列函数, 使用一个简单取模法
  100. public int hashFun(int id) {
  101. return id % size;
  102. }
  103. }
  104. //表示一个雇员
  105. class Emp {
  106. public int id;
  107. public String name;
  108. public Emp next; //next 默认为null
  109. public Emp last;
  110. public Emp(int id, String name) {
  111. super();
  112. this.id = id;
  113. this.name = name;
  114. }
  115. }
  116. //创建EmpLinkedList ,表示链表
  117. class EmpLinkedList {
  118. //头指针,执行第一个Emp,因此我们这个链表的head 是直接指向第一个Emp
  119. private Emp head; //默认null
  120. //添加雇员到链表
  121. //说明
  122. //1. 假定,当添加雇员时,id 是自增长,即id 的分配总是从小到大
  123. // 因此我们将该雇员直接加入到本链表的最后即可
  124. public void add(Emp emp) {
  125. //如果是添加第一个雇员
  126. if (head == null) {
  127. head = emp;
  128. return;
  129. }
  130. //如果不是第一个雇员,则使用一个辅助的指针,帮助定位到最后
  131. Emp curEmp = head;
  132. while (true) {
  133. if (curEmp.next == null) {//说明到链表最后
  134. break;
  135. }
  136. curEmp = curEmp.next; //后移
  137. }
  138. //退出时直接将emp 加入链表
  139. curEmp.next = emp;
  140. }
  141. //遍历链表的雇员信息
  142. public void list(int no) {
  143. if (head == null) { //说明链表为空
  144. System.out.println("第" + (no + 1) + " 链表为空");
  145. return;
  146. }
  147. System.out.print("第" + (no + 1) + " 链表的信息为");
  148. Emp curEmp = head; //辅助指针
  149. while (true) {
  150. System.out.printf(" => id=%d name=%s\t", curEmp.id, curEmp.name);
  151. if (curEmp.next == null) {//说明curEmp 已经是最后结点
  152. break;
  153. }
  154. curEmp = curEmp.next; //后移,遍历
  155. }
  156. System.out.println();
  157. }
  158. //根据id 查找雇员
  159. //如果查找到,就返回Emp, 如果没有找到,就返回null
  160. public Emp findEmpById(int id) {
  161. //判断链表是否为空
  162. if (head == null) {
  163. System.out.println("链表为空");
  164. return null;
  165. }
  166. //辅助指针
  167. Emp curEmp = head;
  168. while (true) {
  169. if (curEmp.id == id) {//找到
  170. break;//这时curEmp 就指向要查找的雇员
  171. }
  172. //退出
  173. if (curEmp.next == null) {//说明遍历当前链表没有找到该雇员
  174. curEmp = null;
  175. break;
  176. }
  177. curEmp = curEmp.next;//以后
  178. }
  179. return curEmp;
  180. }
  181. //根据id 删除雇员
  182. //如果查找到,就删除并返回新链表, 如果没有找到,就返回null
  183. public Emp delEmpByID(int id) {
  184. //判断链表是否为空
  185. if (head == null) {
  186. System.out.println("链表为空");
  187. return null;
  188. }
  189. Emp curEmp = head;
  190. while (true) {
  191. if(curEmp.id ==id){
  192. curEmp = null;
  193. System.out.println("已删除");
  194. break;
  195. }
  196. curEmp = curEmp.next;
  197. if (curEmp.next == null) {//说明遍历当前链表没有找到该雇员
  198. System.out.println("找不到该雇员");
  199. break;
  200. }
  201. }
  202. return curEmp;
  203. }
  204. }

6)运行结果

add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统
add
输入id
1
输入名字
Tom
add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统
add
输入id
2
输入名字
Zack
add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统
add
输入id
456
输入名字
Littie
add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统
find
请输入要查找的id
2
在第3 条链表中找到雇员id = 2
add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统
find
请输入要查找的id
3
链表为空
在哈希表中,没有找到该雇员~
add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统
list
第1 链表为空
第2 链表的信息为 => id=1 name=Tom     => id=456 name=Littie    
第3 链表的信息为 => id=2 name=Zack    
第4 链表为空
第5 链表为空
第6 链表为空
第7 链表为空
add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统
dele
请输入要删除的id
2
已删除
删除后哈希表为null
add: 添加雇员
list: 显示雇员
find: 查找雇员
dele: 查找雇员
exit: 退出系统

知识点小补充:
1.Java中static关键字的作用
2.java中void关键字的作用
3.java中void方法return的用法
4.public static void main(String[] args)含义
5.java中构造函数的作用
6.Java Scanner类的常用方法及用法(很详细)
如.next .nextInt .close