文章目錄
數據結構之哈希表
1. 哈希表的基本介紹
散列表(
Hashtable
,也叫哈希表),是根據關鍵碼值(Key value
)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。
這個映射函數叫做散列函數,存放記錄的數組叫做散列表。
2. 需求分析
哈希表(散列)-Google 上機題
有一個公司,當有新的員工來報道時,要求將該員工的信息加入(id,性別,年齡,住址…),當輸入該員工的id時,要求查找到該員工的 所有信息。
要求:
不使用數據庫,儘量節省內存,速度越快越好--->哈希表(散列)
使用鏈表來實現哈希表, 該鏈表不帶表頭[即: 鏈表的第一個結點就存放僱員信息]
3. 代碼實現
3.1 構建Emp
類
/**
* 僱員類
*
* @author DuanChaojie
* @date 2020年3月12日 下午6:27:10
* @version 1.0
*/
class Emp {
public int id;// 員工id
public String name;// 員工姓名
public Emp next;// next默認爲null
public Emp(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
3.2 創建EmpLinkedList
表示鏈表
/**
* 創建EmpLinkedList表示鏈表
*
* @author DuanChaojie
* @date 2020年3月12日 下午6:35:00
* @version 1.0
*/
class EmpLinkedList {
// 頭指針,執行第一個Emp,因此我們這個鏈表的head是直接指向第一個Emp
private Emp head;// 默認爲null
/*
* 添加僱員到鏈表 1. 假定,當添加僱員時,id 是自增長,即 id 的分配總是從小到大 因此我們將該僱員直接加入到本鏈表的最後即可
*
* @param emp
*/
public void add(Emp emp) {
if (head == null) {// 如果是添加第一個僱員
head = emp;
return;
}
// 如果不是第一個僱員,則使用一個輔助的指針,幫助定位到最後
Emp curEmp = head;
while (true) {
if (curEmp.next == null) {// 說明到鏈表最後
break;
}
curEmp = curEmp.next;// 後移
}
// 退出時直接將emp加入到鏈表
curEmp.next = emp;
}
/*
* 遍歷鏈表的僱員信息
*/
public void list(int no) {
if (head == null) { // 說明鏈表爲空
System.out.println("第" + (no + 1) + "鏈表爲空");
return;
}
System.out.println("第" + (no + 1) + "鏈表的信息爲:");
Emp curEmp = head;// 輔助指針
while (true) {
System.out.printf("id = %d name = %s\t", curEmp.id, curEmp.name);
if (curEmp.next == null) {// 說明 curEmp 已經是最後結點
break;
}
curEmp = curEmp.next;// 後移遍歷
}
System.out.println();
}
/*
* 根據 id 查找僱員 如果查找到,就返回 Emp, 如果沒有找到,就返回 null
*/
public Emp findEmpById(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;
}
}
3.3 創建HashTab
管理多條鏈表
/**
* 創建HashTab管理多條鏈表
*
* @author DuanChaojie
* @date 2020年3月12日 下午6:26:16
* @version 1.0
*/
class HashTab {
private EmpLinkedList[] empLinkedListArray;
private int size;
/*
* 構造器
*/
public HashTab(int size) {
super();
this.size = size;
// 初始化哈希槽
empLinkedListArray = new EmpLinkedList[size];
// 不要忘了初始化,槽裏面的鏈表
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() {
for (int i = 0; i < size; i++) {
empLinkedListArray[i].list(i);
}
}
/*
* 根據輸入的 id,查找僱員
*/
public void findEmpById(int id) {
// 使用散列函數確定到哪條鏈表查找
int empLinkedListNo = hashFun(id);
Emp emp = empLinkedListArray[empLinkedListNo].findEmpById(id);
if (emp != null) {// 找到
System.out.printf("在第%d條鏈表中找到僱員,其id=%d\n", (empLinkedListNo + 1), id);
} else {
System.out.println("在哈希表中,沒有找到該僱員");
}
}
/**
* 散列函數,使用一個簡單取模法
* JDK的散列表對其進行更優處理
* @param id
* @return
*/
public int hashFun(int id) {
return id % size;
}
}
3.4 測試結果
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");
id = scanner.nextInt();
hashTab.findEmpById(id);
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}