原文鏈接傳送門
哈希表(散列)-Google上機題
看一個實際需求,google公司的一個上機題:
有一個公司,當有新的員工來報道時,要求將該員工的信息加入(id,性別,年齡,住址…),當輸入該員工的id時,要求查找到該員工的 所有信息.
要求: 不使用數據庫,盡量節省內存,速度越快越好=>哈希表(散列)
散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。 15 111 % 15
哈希表就是數組里面存儲鏈表
google公司的一個上機題:
有一個公司,當有新的員工來報道時,要求將該員工的信息加入(id,性別,年齡,名字,住址…),當輸入該員工的id時,要求查找到該員工的 所有信息.
要求:
不使用數據庫,速度越快越好=>哈希表(散列) 添加時,保證按照id從低到高插入 [課后思考:如果id不是從低到高插入,但要求各條鏈表仍是從低到高,怎么解決?]
- 使用鏈表來實現哈希表, 該鏈表不帶表頭[即: 鏈表的第一個結點就存放雇員信息]
- 思路分析并畫出示意圖
- 代碼實現[增刪改查(顯示所有員工,按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;}
}
擴展
刪除功能???
原文鏈接傳送門