一、概念
也稱散列表,是指根據關鍵碼值(key-value)而直接進行訪問的數據結構,也就是說它通過把關鍵碼值映射到表中的一個位置來訪問記錄,以加快查找的速度,這個映射函數叫做散列函數,存放記錄的數組叫做散列表,也稱哈希表。
二、一句話概括
哈希表就是由數組和鏈表二者結合而成的新型數據結構。數組存放key(經過hash計算後生成的存放key的數組位置),鏈表存放value。
三、圖解
hash算法有很多,比如最簡單的哈希算法就是用存放的值去取模數組長度。比如存放元素20,那就是20%5=0,所以20這個元素就存放在數組下標爲0的位置,再比如4%5=4,所以元素4存放在數組下標爲4的位置。
回想下Java的hashmap,典型的key-value,那麼我們put的時候是存放到哪了呢?其實就是key的hash算法得到一個數組下標位置,將其存進去。當然比我說的複雜的多,但是大致原理確是如此。
當我們getKey的時候,首先會根據key進行hash算法得到數組下標位置,查找時間複雜度爲O(1)。
四、哈希碰撞
多個key經過hash算法後得到的是同一個值(同一個數組下標),這就稱之爲哈希碰撞。這時候可以將他們的值都存放在鏈表中。(畢竟鏈表是拉鍊式的,可以往後next next next)
五、Google上機題
Google機試題。
就是讓你存儲員工信息,員工包含id和name,要求能高效率的根據id迅速查找到對應的記錄,題目額外要求不能用mysql、redis、hashmap等存儲介質。
分析:很明顯,這道題是再考我們哈希表結構,看我們是否懂hashmap內部存儲的原理以及結構是怎樣的。
思路:首先要有員工類、員工鏈表類、hashtable類(管理多條鏈表的類,也就是鏈表數組類)。總的思路就是根據員工id進行hash算法定位到存儲的位置,然後將員工類對象信息存儲到上一步定位的位置的鏈表中。
六、coding
package com.chentongwei.struct.hashtable;
import java.util.Scanner;
/**
* Description:
*
* @author TongWei.Chen 2019-12-18 15:48:01
*/
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("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);
hashTable.add(emp);
break;
case "list":
hashTable.list();
break;
case "find":
System.out.println("請輸入id");
int findId = scanner.nextInt();
hashTable.findEmpById(findId);
break;
case "exit":
scanner.close();
System.exit(0);
default:
break;
}
}
}
}
// 創建HashTable,管理多條鏈表
class HashTable {
private EmpLinkedList[] empLinkedListArray;
// 表示有多少條鏈表
private int size;
public HashTable(int size) {
// 初始化鏈表
empLinkedListArray = new EmpLinkedList[size];
this.size = size;
for (int i = 0; i < empLinkedListArray.length; i ++) {
empLinkedListArray[i] = new EmpLinkedList();
}
}
// 添加僱員
public void add (Emp emp) {
// 根據員工id,得到該員工應該添加到哪條鏈表上
int empLinkedListNo = hash(emp.id);
// 將emp加入到對應的鏈表中
empLinkedListArray[empLinkedListNo].add(emp);
}
// 遍歷所有鏈表,遍歷hash表
public void list () {
for (int i = 0; i < size; i ++) {
empLinkedListArray[i].list(i);
}
}
// 輸入id查找emp
public void findEmpById(int no) {
int hash = hash(no);
Emp emp = empLinkedListArray[hash].findEmpById(no);
if (emp != null) {
System.out.printf("在第%d條鏈表中找到該僱員, id = %d\n", (hash + 1), no);
} else {
System.out.println("再哈希表中沒找到該僱員");
}
}
// 編寫一個散列函數,採取最簡單的取模法
public int hash (int id) {
return id % size;
}
}
// 創建鏈表
class EmpLinkedList {
// 頭指針,指向第一個Emp,因此我們這個鏈表的head是直接指向第一個Emp
private Emp head;
// 添加僱員到鏈表
// 說明:
// 1.假定添加僱員就直接往鏈表後面追加,不考慮按照僱員id排序
// 2.因此我們將該僱員直接加入到本鏈表的最後一個即可
public void add (Emp emp) {
if (head == null) {
head = emp;
return;
}
// 輔助指針幫助我們定位到鏈表最後
Emp tmp = head;
while (true) {
if (tmp.next == null) {
break;
}
// 後移一位,直到最後
tmp = tmp.next;
}
// 加入到鏈表最後
tmp.next = emp;
}
// 遍歷鏈表的僱員信息
public void list (int no) {
if (head == null) {
System.out.println("第" + (no + 1) + "條鏈表爲空");
return;
}
Emp tmp = head;
System.out.print("第" + (no + 1) + "條鏈表的信息爲:");
while (true) {
System.out.printf("=> id = %d name=%s\t", tmp.id, tmp.name);
if (tmp.next == null) {
break;
}
tmp = tmp.next;
}
System.out.println();
}
// 根據id查找員工信息
public Emp findEmpById (int id) {
// 鏈表爲空
if (head == null) {
System.out.println("鏈表爲空");
return null;
}
// 創建輔助指針
Emp tmp = head;
while (true) {
// 若找到了則break
if (tmp.id == id) {
break;
}
// 代表遍歷完當前鏈表沒找到該僱員
if (tmp.next == null) {
tmp = null;
break;
}
tmp = tmp.next;
}
return tmp;
}
}
// 充當鏈表中的Node節點的作用
class Emp {
public int id;
public String name;
public Emp next;
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
}
結果
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統
add
請輸入id
0
輸入名字
tom
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統
add
請輸入id
1
輸入名字
jim
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統
add
請輸入id
2
輸入名字
pom
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統
add
請輸入id
3
輸入名字
angle
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統
add
請輸入id
5
輸入名字
haha
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統
list
第1條鏈表的信息爲:=> id = 0 name=tom => id = 5 name=haha
第2條鏈表的信息爲:=> id = 1 name=jim
第3條鏈表的信息爲:=> id = 2 name=pom
第4條鏈表的信息爲:=> id = 3 name=angle
第5條鏈表爲空
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統
find
請輸入id
5
在第1條鏈表中找到該僱員, id = 5
add:添加僱員
list:遍歷僱員
find:查找僱員
exit:退出系統